mxHierarchicalLayout.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. /**
  2. * Copyright (c) 2006-2018, JGraph Ltd
  3. * Copyright (c) 2006-2018, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxHierarchicalLayout
  7. *
  8. * A hierarchical layout algorithm.
  9. *
  10. * Constructor: mxHierarchicalLayout
  11. *
  12. * Constructs a new hierarchical layout algorithm.
  13. *
  14. * Arguments:
  15. *
  16. * graph - Reference to the enclosing <mxGraph>.
  17. * orientation - Optional constant that defines the orientation of this
  18. * layout.
  19. * deterministic - Optional boolean that specifies if this layout should be
  20. * deterministic. Default is true.
  21. */
  22. function mxHierarchicalLayout(graph, orientation, deterministic)
  23. {
  24. mxGraphLayout.call(this, graph);
  25. this.orientation = (orientation != null) ? orientation : mxConstants.DIRECTION_NORTH;
  26. this.deterministic = (deterministic != null) ? deterministic : true;
  27. };
  28. var mxHierarchicalEdgeStyle =
  29. {
  30. ORTHOGONAL: 1,
  31. POLYLINE: 2,
  32. STRAIGHT: 3,
  33. CURVE: 4
  34. };
  35. /**
  36. * Extends mxGraphLayout.
  37. */
  38. mxHierarchicalLayout.prototype = new mxGraphLayout();
  39. mxHierarchicalLayout.prototype.constructor = mxHierarchicalLayout;
  40. /**
  41. * Variable: roots
  42. *
  43. * Holds the array of <mxCell> that this layout contains.
  44. */
  45. mxHierarchicalLayout.prototype.roots = null;
  46. /**
  47. * Variable: resizeParent
  48. *
  49. * Specifies if the parent should be resized after the layout so that it
  50. * contains all the child cells. Default is false. See also <parentBorder>.
  51. */
  52. mxHierarchicalLayout.prototype.resizeParent = false;
  53. /**
  54. * Variable: maintainParentLocation
  55. *
  56. * Specifies if the parent location should be maintained, so that the
  57. * top, left corner stays the same before and after execution of
  58. * the layout. Default is false for backwards compatibility.
  59. */
  60. mxHierarchicalLayout.prototype.maintainParentLocation = false;
  61. /**
  62. * Variable: moveParent
  63. *
  64. * Specifies if the parent should be moved if <resizeParent> is enabled.
  65. * Default is false.
  66. */
  67. mxHierarchicalLayout.prototype.moveParent = false;
  68. /**
  69. * Variable: parentBorder
  70. *
  71. * The border to be added around the children if the parent is to be
  72. * resized using <resizeParent>. Default is 0.
  73. */
  74. mxHierarchicalLayout.prototype.parentBorder = 0;
  75. /**
  76. * Variable: intraCellSpacing
  77. *
  78. * The spacing buffer added between cells on the same layer. Default is 30.
  79. */
  80. mxHierarchicalLayout.prototype.intraCellSpacing = 30;
  81. /**
  82. * Variable: interRankCellSpacing
  83. *
  84. * The spacing buffer added between cell on adjacent layers. Default is 100.
  85. */
  86. mxHierarchicalLayout.prototype.interRankCellSpacing = 100;
  87. /**
  88. * Variable: interHierarchySpacing
  89. *
  90. * The spacing buffer between unconnected hierarchies. Default is 60.
  91. */
  92. mxHierarchicalLayout.prototype.interHierarchySpacing = 60;
  93. /**
  94. * Variable: parallelEdgeSpacing
  95. *
  96. * The distance between each parallel edge on each ranks for long edges.
  97. * Default is 10.
  98. */
  99. mxHierarchicalLayout.prototype.parallelEdgeSpacing = 10;
  100. /**
  101. * Variable: orientation
  102. *
  103. * The position of the root node(s) relative to the laid out graph in.
  104. * Default is <mxConstants.DIRECTION_NORTH>.
  105. */
  106. mxHierarchicalLayout.prototype.orientation = mxConstants.DIRECTION_NORTH;
  107. /**
  108. * Variable: fineTuning
  109. *
  110. * Whether or not to perform local optimisations and iterate multiple times
  111. * through the algorithm. Default is true.
  112. */
  113. mxHierarchicalLayout.prototype.fineTuning = true;
  114. /**
  115. *
  116. * Variable: tightenToSource
  117. *
  118. * Whether or not to tighten the assigned ranks of vertices up towards
  119. * the source cells. Default is true.
  120. */
  121. mxHierarchicalLayout.prototype.tightenToSource = true;
  122. /**
  123. * Variable: disableEdgeStyle
  124. *
  125. * Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
  126. * modified by the result. Default is true.
  127. */
  128. mxHierarchicalLayout.prototype.disableEdgeStyle = true;
  129. /**
  130. * Variable: traverseAncestors
  131. *
  132. * Whether or not to drill into child cells and layout in reverse
  133. * group order. This also cause the layout to navigate edges whose
  134. * terminal vertices have different parents but are in the same
  135. * ancestry chain. Default is true.
  136. */
  137. mxHierarchicalLayout.prototype.traverseAncestors = true;
  138. /**
  139. * Variable: model
  140. *
  141. * The internal <mxGraphHierarchyModel> formed of the layout.
  142. */
  143. mxHierarchicalLayout.prototype.model = null;
  144. /**
  145. * Variable: edgesSet
  146. *
  147. * A cache of edges whose source terminal is the key
  148. */
  149. mxHierarchicalLayout.prototype.edgesCache = null;
  150. /**
  151. * Variable: edgesSet
  152. *
  153. * A cache of edges whose source terminal is the key
  154. */
  155. mxHierarchicalLayout.prototype.edgeSourceTermCache = null;
  156. /**
  157. * Variable: edgesSet
  158. *
  159. * A cache of edges whose source terminal is the key
  160. */
  161. mxHierarchicalLayout.prototype.edgesTargetTermCache = null;
  162. /**
  163. * Variable: edgeStyle
  164. *
  165. * The style to apply between cell layers to edge segments.
  166. * Default is <mxHierarchicalEdgeStyle.POLYLINE>.
  167. */
  168. mxHierarchicalLayout.prototype.edgeStyle = mxHierarchicalEdgeStyle.POLYLINE;
  169. /**
  170. * Function: getModel
  171. *
  172. * Returns the internal <mxGraphHierarchyModel> for this layout algorithm.
  173. */
  174. mxHierarchicalLayout.prototype.getModel = function()
  175. {
  176. return this.model;
  177. };
  178. /**
  179. * Function: execute
  180. *
  181. * Executes the layout for the children of the specified parent.
  182. *
  183. * Parameters:
  184. *
  185. * parent - Parent <mxCell> that contains the children to be laid out.
  186. * roots - Optional starting roots of the layout.
  187. */
  188. mxHierarchicalLayout.prototype.execute = function(parent, roots)
  189. {
  190. this.parent = parent;
  191. var model = this.graph.model;
  192. this.edgesCache = new mxDictionary();
  193. this.edgeSourceTermCache = new mxDictionary();
  194. this.edgesTargetTermCache = new mxDictionary();
  195. if (roots != null && !(roots instanceof Array))
  196. {
  197. roots = [roots];
  198. }
  199. // If the roots are set and the parent is set, only
  200. // use the roots that are some dependent of the that
  201. // parent.
  202. // If just the root are set, use them as-is
  203. // If just the parent is set use it's immediate
  204. // children as the initial set
  205. if (roots == null && parent == null)
  206. {
  207. // TODO indicate the problem
  208. return;
  209. }
  210. // Maintaining parent location
  211. this.parentX = null;
  212. this.parentY = null;
  213. if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation)
  214. {
  215. var geo = this.graph.getCellGeometry(parent);
  216. if (geo != null)
  217. {
  218. this.parentX = geo.x;
  219. this.parentY = geo.y;
  220. }
  221. }
  222. if (roots != null)
  223. {
  224. var rootsCopy = [];
  225. for (var i = 0; i < roots.length; i++)
  226. {
  227. var ancestor = parent != null ? model.isAncestor(parent, roots[i]) : true;
  228. if (ancestor && model.isVertex(roots[i]))
  229. {
  230. rootsCopy.push(roots[i]);
  231. }
  232. }
  233. this.roots = rootsCopy;
  234. }
  235. model.beginUpdate();
  236. try
  237. {
  238. this.run(parent);
  239. if (this.resizeParent && !this.graph.isCellCollapsed(parent))
  240. {
  241. this.graph.updateGroupBounds([parent], this.parentBorder, this.moveParent);
  242. }
  243. // Maintaining parent location
  244. if (this.parentX != null && this.parentY != null)
  245. {
  246. var geo = this.graph.getCellGeometry(parent);
  247. if (geo != null)
  248. {
  249. geo = geo.clone();
  250. geo.x = this.parentX;
  251. geo.y = this.parentY;
  252. model.setGeometry(parent, geo);
  253. }
  254. }
  255. }
  256. finally
  257. {
  258. model.endUpdate();
  259. }
  260. };
  261. /**
  262. * Function: findRoots
  263. *
  264. * Returns all visible children in the given parent which do not have
  265. * incoming edges. If the result is empty then the children with the
  266. * maximum difference between incoming and outgoing edges are returned.
  267. * This takes into account edges that are being promoted to the given
  268. * root due to invisible children or collapsed cells.
  269. *
  270. * Parameters:
  271. *
  272. * parent - <mxCell> whose children should be checked.
  273. * vertices - array of vertices to limit search to
  274. */
  275. mxHierarchicalLayout.prototype.findRoots = function(parent, vertices)
  276. {
  277. var roots = [];
  278. if (parent != null && vertices != null)
  279. {
  280. var model = this.graph.model;
  281. var best = null;
  282. var maxDiff = -100000;
  283. for (var i in vertices)
  284. {
  285. var cell = vertices[i];
  286. if (model.isVertex(cell) && this.graph.isCellVisible(cell))
  287. {
  288. var conns = this.getEdges(cell);
  289. var fanOut = 0;
  290. var fanIn = 0;
  291. for (var k = 0; k < conns.length; k++)
  292. {
  293. var src = this.getVisibleTerminal(conns[k], true);
  294. if (src == cell)
  295. {
  296. fanOut++;
  297. }
  298. else
  299. {
  300. fanIn++;
  301. }
  302. }
  303. if (fanIn == 0 && fanOut > 0)
  304. {
  305. roots.push(cell);
  306. }
  307. var diff = fanOut - fanIn;
  308. if (diff > maxDiff)
  309. {
  310. maxDiff = diff;
  311. best = cell;
  312. }
  313. }
  314. }
  315. if (roots.length == 0 && best != null)
  316. {
  317. roots.push(best);
  318. }
  319. }
  320. return roots;
  321. };
  322. /**
  323. * Function: getEdges
  324. *
  325. * Returns the connected edges for the given cell.
  326. *
  327. * Parameters:
  328. *
  329. * cell - <mxCell> whose edges should be returned.
  330. */
  331. mxHierarchicalLayout.prototype.getEdges = function(cell)
  332. {
  333. var cachedEdges = this.edgesCache.get(cell);
  334. if (cachedEdges != null)
  335. {
  336. return cachedEdges;
  337. }
  338. var model = this.graph.model;
  339. var edges = [];
  340. var isCollapsed = this.graph.isCellCollapsed(cell);
  341. var childCount = model.getChildCount(cell);
  342. for (var i = 0; i < childCount; i++)
  343. {
  344. var child = model.getChildAt(cell, i);
  345. if (this.isPort(child))
  346. {
  347. edges = edges.concat(model.getEdges(child, true, true));
  348. }
  349. else if (isCollapsed || !this.graph.isCellVisible(child))
  350. {
  351. edges = edges.concat(model.getEdges(child, true, true));
  352. }
  353. }
  354. edges = edges.concat(model.getEdges(cell, true, true));
  355. var result = [];
  356. for (var i = 0; i < edges.length; i++)
  357. {
  358. var source = this.getVisibleTerminal(edges[i], true);
  359. var target = this.getVisibleTerminal(edges[i], false);
  360. if ((source == target) ||
  361. ((source != target) &&
  362. ((target == cell && (this.parent == null || this.isAncestor(this.parent, source, this.traverseAncestors))) ||
  363. (source == cell && (this.parent == null || this.isAncestor(this.parent, target, this.traverseAncestors))))))
  364. {
  365. result.push(edges[i]);
  366. }
  367. }
  368. this.edgesCache.put(cell, result);
  369. return result;
  370. };
  371. /**
  372. * Function: getVisibleTerminal
  373. *
  374. * Helper function to return visible terminal for edge allowing for ports
  375. *
  376. * Parameters:
  377. *
  378. * edge - <mxCell> whose edges should be returned.
  379. * source - Boolean that specifies whether the source or target terminal is to be returned
  380. */
  381. mxHierarchicalLayout.prototype.getVisibleTerminal = function(edge, source)
  382. {
  383. var terminalCache = this.edgesTargetTermCache;
  384. if (source)
  385. {
  386. terminalCache = this.edgeSourceTermCache;
  387. }
  388. var term = terminalCache.get(edge);
  389. if (term != null)
  390. {
  391. return term;
  392. }
  393. var state = this.graph.view.getState(edge);
  394. var terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
  395. if (terminal == null)
  396. {
  397. terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
  398. }
  399. if (terminal != null)
  400. {
  401. if (this.isPort(terminal))
  402. {
  403. terminal = this.graph.model.getParent(terminal);
  404. }
  405. terminalCache.put(edge, terminal);
  406. }
  407. return terminal;
  408. };
  409. /**
  410. * Function: run
  411. *
  412. * The API method used to exercise the layout upon the graph description
  413. * and produce a separate description of the vertex position and edge
  414. * routing changes made. It runs each stage of the layout that has been
  415. * created.
  416. */
  417. mxHierarchicalLayout.prototype.run = function(parent)
  418. {
  419. // Separate out unconnected hierarchies
  420. var hierarchyVertices = [];
  421. var allVertexSet = [];
  422. if (this.roots == null && parent != null)
  423. {
  424. var filledVertexSet = Object();
  425. this.filterDescendants(parent, filledVertexSet);
  426. this.roots = [];
  427. var filledVertexSetEmpty = true;
  428. // Poor man's isSetEmpty
  429. for (var key in filledVertexSet)
  430. {
  431. if (filledVertexSet[key] != null)
  432. {
  433. filledVertexSetEmpty = false;
  434. break;
  435. }
  436. }
  437. while (!filledVertexSetEmpty)
  438. {
  439. var candidateRoots = this.findRoots(parent, filledVertexSet);
  440. // If the candidate root is an unconnected group cell, remove it from
  441. // the layout. We may need a custom set that holds such groups and forces
  442. // them to be processed for resizing and/or moving.
  443. for (var i = 0; i < candidateRoots.length; i++)
  444. {
  445. var vertexSet = Object();
  446. hierarchyVertices.push(vertexSet);
  447. this.traverse(candidateRoots[i], true, null, allVertexSet, vertexSet,
  448. hierarchyVertices, filledVertexSet);
  449. }
  450. for (var i = 0; i < candidateRoots.length; i++)
  451. {
  452. this.roots.push(candidateRoots[i]);
  453. }
  454. filledVertexSetEmpty = true;
  455. // Poor man's isSetEmpty
  456. for (var key in filledVertexSet)
  457. {
  458. if (filledVertexSet[key] != null)
  459. {
  460. filledVertexSetEmpty = false;
  461. break;
  462. }
  463. }
  464. }
  465. }
  466. else
  467. {
  468. // Find vertex set as directed traversal from roots
  469. for (var i = 0; i < this.roots.length; i++)
  470. {
  471. var vertexSet = Object();
  472. hierarchyVertices.push(vertexSet);
  473. this.traverse(this.roots[i], true, null, allVertexSet, vertexSet,
  474. hierarchyVertices, null);
  475. }
  476. }
  477. // Iterate through the result removing parents who have children in this layout
  478. // Perform a layout for each seperate hierarchy
  479. // Track initial coordinate x-positioning
  480. var initialX = 0;
  481. for (var i = 0; i < hierarchyVertices.length; i++)
  482. {
  483. var vertexSet = hierarchyVertices[i];
  484. var tmp = [];
  485. for (var key in vertexSet)
  486. {
  487. tmp.push(vertexSet[key]);
  488. }
  489. this.model = new mxGraphHierarchyModel(this, tmp, this.roots,
  490. parent, this.tightenToSource);
  491. this.cycleStage(parent);
  492. this.layeringStage();
  493. this.crossingStage(parent);
  494. initialX = this.placementStage(initialX, parent);
  495. }
  496. };
  497. /**
  498. * Function: filterDescendants
  499. *
  500. * Creates an array of descendant cells
  501. */
  502. mxHierarchicalLayout.prototype.filterDescendants = function(cell, result)
  503. {
  504. var model = this.graph.model;
  505. if (model.isVertex(cell) && cell != this.parent && this.graph.isCellVisible(cell))
  506. {
  507. result[mxObjectIdentity.get(cell)] = cell;
  508. }
  509. if (this.traverseAncestors || cell == this.parent
  510. && this.graph.isCellVisible(cell))
  511. {
  512. var childCount = model.getChildCount(cell);
  513. for (var i = 0; i < childCount; i++)
  514. {
  515. var child = model.getChildAt(cell, i);
  516. // Ignore ports in the layout vertex list, they are dealt with
  517. // in the traversal mechanisms
  518. if (!this.isPort(child))
  519. {
  520. this.filterDescendants(child, result);
  521. }
  522. }
  523. }
  524. };
  525. /**
  526. * Function: isPort
  527. *
  528. * Returns true if the given cell is a "port", that is, when connecting to
  529. * it, its parent is the connecting vertex in terms of graph traversal
  530. *
  531. * Parameters:
  532. *
  533. * cell - <mxCell> that represents the port.
  534. */
  535. mxHierarchicalLayout.prototype.isPort = function(cell)
  536. {
  537. if (cell != null && cell.geometry != null)
  538. {
  539. return cell.geometry.relative;
  540. }
  541. else
  542. {
  543. return false;
  544. }
  545. };
  546. /**
  547. * Function: getEdgesBetween
  548. *
  549. * Returns the edges between the given source and target. This takes into
  550. * account collapsed and invisible cells and ports.
  551. *
  552. * Parameters:
  553. *
  554. * source -
  555. * target -
  556. * directed -
  557. */
  558. mxHierarchicalLayout.prototype.getEdgesBetween = function(source, target, directed)
  559. {
  560. directed = (directed != null) ? directed : false;
  561. var edges = this.getEdges(source);
  562. var result = [];
  563. // Checks if the edge is connected to the correct
  564. // cell and returns the first match
  565. for (var i = 0; i < edges.length; i++)
  566. {
  567. var src = this.getVisibleTerminal(edges[i], true);
  568. var trg = this.getVisibleTerminal(edges[i], false);
  569. if ((src == source && trg == target) || (!directed && src == target && trg == source))
  570. {
  571. result.push(edges[i]);
  572. }
  573. }
  574. return result;
  575. };
  576. /**
  577. * Traverses the (directed) graph invoking the given function for each
  578. * visited vertex and edge. The function is invoked with the current vertex
  579. * and the incoming edge as a parameter. This implementation makes sure
  580. * each vertex is only visited once. The function may return false if the
  581. * traversal should stop at the given vertex.
  582. *
  583. * Parameters:
  584. *
  585. * vertex - <mxCell> that represents the vertex where the traversal starts.
  586. * directed - boolean indicating if edges should only be traversed
  587. * from source to target. Default is true.
  588. * edge - Optional <mxCell> that represents the incoming edge. This is
  589. * null for the first step of the traversal.
  590. * allVertices - Array of cell paths for the visited cells.
  591. */
  592. mxHierarchicalLayout.prototype.traverse = function(vertex, directed, edge, allVertices, currentComp,
  593. hierarchyVertices, filledVertexSet)
  594. {
  595. if (vertex != null && allVertices != null)
  596. {
  597. // Has this vertex been seen before in any traversal
  598. // And if the filled vertex set is populated, only
  599. // process vertices in that it contains
  600. var vertexID = mxObjectIdentity.get(vertex);
  601. if ((allVertices[vertexID] == null)
  602. && (filledVertexSet == null ? true : filledVertexSet[vertexID] != null))
  603. {
  604. if (currentComp[vertexID] == null)
  605. {
  606. currentComp[vertexID] = vertex;
  607. }
  608. if (allVertices[vertexID] == null)
  609. {
  610. allVertices[vertexID] = vertex;
  611. }
  612. if (filledVertexSet !== null)
  613. {
  614. delete filledVertexSet[vertexID];
  615. }
  616. var edges = this.getEdges(vertex);
  617. var edgeIsSource = [];
  618. for (var i = 0; i < edges.length; i++)
  619. {
  620. edgeIsSource[i] = (this.getVisibleTerminal(edges[i], true) == vertex);
  621. }
  622. for (var i = 0; i < edges.length; i++)
  623. {
  624. if (!directed || edgeIsSource[i])
  625. {
  626. var next = this.getVisibleTerminal(edges[i], !edgeIsSource[i]);
  627. // Check whether there are more edges incoming from the target vertex than outgoing
  628. // The hierarchical model treats bi-directional parallel edges as being sourced
  629. // from the more "sourced" terminal. If the directions are equal in number, the direction
  630. // is that of the natural direction from the roots of the layout.
  631. // The checks below are slightly more verbose than need be for performance reasons
  632. var netCount = 1;
  633. for (var j = 0; j < edges.length; j++)
  634. {
  635. if (j == i)
  636. {
  637. continue;
  638. }
  639. else
  640. {
  641. var isSource2 = edgeIsSource[j];
  642. var otherTerm = this.getVisibleTerminal(edges[j], !isSource2);
  643. if (otherTerm == next)
  644. {
  645. if (isSource2)
  646. {
  647. netCount++;
  648. }
  649. else
  650. {
  651. netCount--;
  652. }
  653. }
  654. }
  655. }
  656. if (netCount >= 0)
  657. {
  658. currentComp = this.traverse(next, directed, edges[i], allVertices,
  659. currentComp, hierarchyVertices,
  660. filledVertexSet);
  661. }
  662. }
  663. }
  664. }
  665. else
  666. {
  667. if (currentComp[vertexID] == null)
  668. {
  669. // We've seen this vertex before, but not in the current component
  670. // This component and the one it's in need to be merged
  671. for (var i = 0; i < hierarchyVertices.length; i++)
  672. {
  673. var comp = hierarchyVertices[i];
  674. if (comp[vertexID] != null)
  675. {
  676. for (var key in comp)
  677. {
  678. currentComp[key] = comp[key];
  679. }
  680. // Remove the current component from the hierarchy set
  681. hierarchyVertices.splice(i, 1);
  682. return currentComp;
  683. }
  684. }
  685. }
  686. }
  687. }
  688. return currentComp;
  689. };
  690. /**
  691. * Function: cycleStage
  692. *
  693. * Executes the cycle stage using mxMinimumCycleRemover.
  694. */
  695. mxHierarchicalLayout.prototype.cycleStage = function(parent)
  696. {
  697. var cycleStage = new mxMinimumCycleRemover(this);
  698. cycleStage.execute(parent);
  699. };
  700. /**
  701. * Function: layeringStage
  702. *
  703. * Implements first stage of a Sugiyama layout.
  704. */
  705. mxHierarchicalLayout.prototype.layeringStage = function()
  706. {
  707. this.model.initialRank();
  708. this.model.fixRanks();
  709. };
  710. /**
  711. * Function: crossingStage
  712. *
  713. * Executes the crossing stage using mxMedianHybridCrossingReduction.
  714. */
  715. mxHierarchicalLayout.prototype.crossingStage = function(parent)
  716. {
  717. var crossingStage = new mxMedianHybridCrossingReduction(this);
  718. crossingStage.execute(parent);
  719. };
  720. /**
  721. * Function: placementStage
  722. *
  723. * Executes the placement stage using mxCoordinateAssignment.
  724. */
  725. mxHierarchicalLayout.prototype.placementStage = function(initialX, parent)
  726. {
  727. var placementStage = new mxCoordinateAssignment(this, this.intraCellSpacing,
  728. this.interRankCellSpacing, this.orientation, initialX,
  729. this.parallelEdgeSpacing);
  730. placementStage.fineTuning = this.fineTuning;
  731. placementStage.execute(parent);
  732. return placementStage.limitX + this.interHierarchySpacing;
  733. };
  734. __mxOutput.mxHierarchicalLayout = typeof mxHierarchicalLayout !== 'undefined' ? mxHierarchicalLayout : undefined;