mxGraphLayout.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /**
  2. * Copyright (c) 2006-2018, JGraph Ltd
  3. * Copyright (c) 2006-2018, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxGraphLayout
  7. *
  8. * Base class for all layout algorithms in mxGraph. Main public functions are
  9. * <moveCell> for handling a moved cell within a layouted parent, and <execute> for
  10. * running the layout on a given parent cell.
  11. *
  12. * Known Subclasses:
  13. *
  14. * <mxCircleLayout>, <mxCompactTreeLayout>, <mxCompositeLayout>,
  15. * <mxFastOrganicLayout>, <mxParallelEdgeLayout>, <mxPartitionLayout>,
  16. * <mxStackLayout>
  17. *
  18. * Constructor: mxGraphLayout
  19. *
  20. * Constructs a new layout using the given layouts.
  21. *
  22. * Arguments:
  23. *
  24. * graph - Enclosing
  25. */
  26. function mxGraphLayout(graph)
  27. {
  28. this.graph = graph;
  29. };
  30. /**
  31. * Variable: graph
  32. *
  33. * Reference to the enclosing <mxGraph>.
  34. */
  35. mxGraphLayout.prototype.graph = null;
  36. /**
  37. * Variable: useBoundingBox
  38. *
  39. * Boolean indicating if the bounding box of the label should be used if
  40. * its available. Default is true.
  41. */
  42. mxGraphLayout.prototype.useBoundingBox = true;
  43. /**
  44. * Variable: parent
  45. *
  46. * The parent cell of the layout, if any
  47. */
  48. mxGraphLayout.prototype.parent = null;
  49. /**
  50. * Function: moveCell
  51. *
  52. * Notified when a cell is being moved in a parent that has automatic
  53. * layout to update the cell state (eg. index) so that the outcome of the
  54. * layout will position the vertex as close to the point (x, y) as
  55. * possible.
  56. *
  57. * Empty implementation.
  58. *
  59. * Parameters:
  60. *
  61. * cell - <mxCell> which has been moved.
  62. * x - X-coordinate of the new cell location.
  63. * y - Y-coordinate of the new cell location.
  64. */
  65. mxGraphLayout.prototype.moveCell = function(cell, x, y) { };
  66. /**
  67. * Function: resizeCell
  68. *
  69. * Notified when a cell is being resized in a parent that has automatic
  70. * layout to update the other cells in the layout.
  71. *
  72. * Empty implementation.
  73. *
  74. * Parameters:
  75. *
  76. * cell - <mxCell> which has been moved.
  77. * bounds - <mxRectangle> that represents the new cell bounds.
  78. */
  79. mxGraphLayout.prototype.resizeCell = function(cell, bounds) { };
  80. /**
  81. * Function: execute
  82. *
  83. * Executes the layout algorithm for the children of the given parent.
  84. *
  85. * Parameters:
  86. *
  87. * parent - <mxCell> whose children should be layed out.
  88. */
  89. mxGraphLayout.prototype.execute = function(parent) { };
  90. /**
  91. * Function: getGraph
  92. *
  93. * Returns the graph that this layout operates on.
  94. */
  95. mxGraphLayout.prototype.getGraph = function()
  96. {
  97. return this.graph;
  98. };
  99. /**
  100. * Function: getConstraint
  101. *
  102. * Returns the constraint for the given key and cell. The optional edge and
  103. * source arguments are used to return inbound and outgoing routing-
  104. * constraints for the given edge and vertex. This implementation always
  105. * returns the value for the given key in the style of the given cell.
  106. *
  107. * Parameters:
  108. *
  109. * key - Key of the constraint to be returned.
  110. * cell - <mxCell> whose constraint should be returned.
  111. * edge - Optional <mxCell> that represents the connection whose constraint
  112. * should be returned. Default is null.
  113. * source - Optional boolean that specifies if the connection is incoming
  114. * or outgoing. Default is null.
  115. */
  116. mxGraphLayout.prototype.getConstraint = function(key, cell, edge, source)
  117. {
  118. return this.graph.getCurrentCellStyle(cell)[key]
  119. };
  120. /**
  121. * Function: traverse
  122. *
  123. * Traverses the (directed) graph invoking the given function for each
  124. * visited vertex and edge. The function is invoked with the current vertex
  125. * and the incoming edge as a parameter. This implementation makes sure
  126. * each vertex is only visited once. The function may return false if the
  127. * traversal should stop at the given vertex.
  128. *
  129. * Example:
  130. *
  131. * (code)
  132. * mxLog.show();
  133. * var cell = graph.getSelectionCell();
  134. * graph.traverse(cell, false, function(vertex, edge)
  135. * {
  136. * mxLog.debug(graph.getLabel(vertex));
  137. * });
  138. * (end)
  139. *
  140. * Parameters:
  141. *
  142. * vertex - <mxCell> that represents the vertex where the traversal starts.
  143. * directed - Optional boolean indicating if edges should only be traversed
  144. * from source to target. Default is true.
  145. * func - Visitor function that takes the current vertex and the incoming
  146. * edge as arguments. The traversal stops if the function returns false.
  147. * edge - Optional <mxCell> that represents the incoming edge. This is
  148. * null for the first step of the traversal.
  149. * visited - Optional <mxDictionary> of cell paths for the visited cells.
  150. */
  151. mxGraphLayout.traverse = function(vertex, directed, func, edge, visited)
  152. {
  153. if (func != null && vertex != null)
  154. {
  155. directed = (directed != null) ? directed : true;
  156. visited = visited || new mxDictionary();
  157. if (!visited.get(vertex))
  158. {
  159. visited.put(vertex, true);
  160. var result = func(vertex, edge);
  161. if (result == null || result)
  162. {
  163. var edgeCount = this.graph.model.getEdgeCount(vertex);
  164. if (edgeCount > 0)
  165. {
  166. for (var i = 0; i < edgeCount; i++)
  167. {
  168. var e = this.graph.model.getEdgeAt(vertex, i);
  169. var isSource = this.graph.model.getTerminal(e, true) == vertex;
  170. if (!directed || isSource)
  171. {
  172. var next = this.graph.view.getVisibleTerminal(e, !isSource);
  173. this.traverse(next, directed, func, e, visited);
  174. }
  175. }
  176. }
  177. }
  178. }
  179. }
  180. };
  181. /**
  182. * Function: isAncestor
  183. *
  184. * Returns true if the given parent is an ancestor of the given child.
  185. *
  186. * Parameters:
  187. *
  188. * parent - <mxCell> that specifies the parent.
  189. * child - <mxCell> that specifies the child.
  190. * traverseAncestors - boolean whether to
  191. */
  192. mxGraphLayout.prototype.isAncestor = function(parent, child, traverseAncestors)
  193. {
  194. if (!traverseAncestors)
  195. {
  196. return (this.graph.model.getParent(child) == parent);
  197. }
  198. if (child == parent)
  199. {
  200. return false;
  201. }
  202. while (child != null && child != parent)
  203. {
  204. child = this.graph.model.getParent(child);
  205. }
  206. return child == parent;
  207. };
  208. /**
  209. * Function: isVertexMovable
  210. *
  211. * Returns a boolean indicating if the given <mxCell> is movable or
  212. * bendable by the algorithm. This implementation returns true if the given
  213. * cell is movable in the graph.
  214. *
  215. * Parameters:
  216. *
  217. * cell - <mxCell> whose movable state should be returned.
  218. */
  219. mxGraphLayout.prototype.isVertexMovable = function(cell)
  220. {
  221. return this.graph.isCellMovable(cell);
  222. };
  223. /**
  224. * Function: isVertexIgnored
  225. *
  226. * Returns a boolean indicating if the given <mxCell> should be ignored by
  227. * the algorithm. This implementation returns false for all vertices.
  228. *
  229. * Parameters:
  230. *
  231. * vertex - <mxCell> whose ignored state should be returned.
  232. */
  233. mxGraphLayout.prototype.isVertexIgnored = function(vertex)
  234. {
  235. return !this.graph.getModel().isVertex(vertex) ||
  236. !this.graph.isCellVisible(vertex);
  237. };
  238. /**
  239. * Function: isEdgeIgnored
  240. *
  241. * Returns a boolean indicating if the given <mxCell> should be ignored by
  242. * the algorithm. This implementation returns false for all vertices.
  243. *
  244. * Parameters:
  245. *
  246. * cell - <mxCell> whose ignored state should be returned.
  247. */
  248. mxGraphLayout.prototype.isEdgeIgnored = function(edge)
  249. {
  250. var model = this.graph.getModel();
  251. return !model.isEdge(edge) ||
  252. !this.graph.isCellVisible(edge) ||
  253. model.getTerminal(edge, true) == null ||
  254. model.getTerminal(edge, false) == null;
  255. };
  256. /**
  257. * Function: setEdgeStyleEnabled
  258. *
  259. * Disables or enables the edge style of the given edge.
  260. */
  261. mxGraphLayout.prototype.setEdgeStyleEnabled = function(edge, value)
  262. {
  263. this.graph.setCellStyles(mxConstants.STYLE_NOEDGESTYLE,
  264. (value) ? '0' : '1', [edge]);
  265. };
  266. /**
  267. * Function: setOrthogonalEdge
  268. *
  269. * Disables or enables orthogonal end segments of the given edge.
  270. */
  271. mxGraphLayout.prototype.setOrthogonalEdge = function(edge, value)
  272. {
  273. this.graph.setCellStyles(mxConstants.STYLE_ORTHOGONAL,
  274. (value) ? '1' : '0', [edge]);
  275. };
  276. /**
  277. * Function: getParentOffset
  278. *
  279. * Determines the offset of the given parent to the parent
  280. * of the layout
  281. */
  282. mxGraphLayout.prototype.getParentOffset = function(parent)
  283. {
  284. var result = new mxPoint();
  285. if (parent != null && parent != this.parent)
  286. {
  287. var model = this.graph.getModel();
  288. if (model.isAncestor(this.parent, parent))
  289. {
  290. var parentGeo = model.getGeometry(parent);
  291. while (parent != this.parent)
  292. {
  293. result.x = result.x + parentGeo.x;
  294. result.y = result.y + parentGeo.y;
  295. parent = model.getParent(parent);;
  296. parentGeo = model.getGeometry(parent);
  297. }
  298. }
  299. }
  300. return result;
  301. };
  302. /**
  303. * Function: setEdgePoints
  304. *
  305. * Replaces the array of mxPoints in the geometry of the given edge
  306. * with the given array of mxPoints.
  307. */
  308. mxGraphLayout.prototype.setEdgePoints = function(edge, points)
  309. {
  310. if (edge != null)
  311. {
  312. var model = this.graph.model;
  313. var geometry = model.getGeometry(edge);
  314. if (geometry == null)
  315. {
  316. geometry = new mxGeometry();
  317. geometry.setRelative(true);
  318. }
  319. else
  320. {
  321. geometry = geometry.clone();
  322. }
  323. if (this.parent != null && points != null)
  324. {
  325. var parent = model.getParent(edge);
  326. var parentOffset = this.getParentOffset(parent);
  327. for (var i = 0; i < points.length; i++)
  328. {
  329. points[i].x = points[i].x - parentOffset.x;
  330. points[i].y = points[i].y - parentOffset.y;
  331. }
  332. }
  333. geometry.points = points;
  334. model.setGeometry(edge, geometry);
  335. }
  336. };
  337. /**
  338. * Function: setVertexLocation
  339. *
  340. * Sets the new position of the given cell taking into account the size of
  341. * the bounding box if <useBoundingBox> is true. The change is only carried
  342. * out if the new location is not equal to the existing location, otherwise
  343. * the geometry is not replaced with an updated instance. The new or old
  344. * bounds are returned (including overlapping labels).
  345. *
  346. * Parameters:
  347. *
  348. * cell - <mxCell> whose geometry is to be set.
  349. * x - Integer that defines the x-coordinate of the new location.
  350. * y - Integer that defines the y-coordinate of the new location.
  351. */
  352. mxGraphLayout.prototype.setVertexLocation = function(cell, x, y)
  353. {
  354. var model = this.graph.getModel();
  355. var geometry = model.getGeometry(cell);
  356. var result = null;
  357. if (geometry != null)
  358. {
  359. result = new mxRectangle(x, y, geometry.width, geometry.height);
  360. // Checks for oversize labels and shifts the result
  361. // TODO: Use mxUtils.getStringSize for label bounds
  362. if (this.useBoundingBox)
  363. {
  364. var state = this.graph.getView().getState(cell);
  365. if (state != null && state.text != null && state.text.boundingBox != null)
  366. {
  367. var scale = this.graph.getView().scale;
  368. var box = state.text.boundingBox;
  369. if (state.text.boundingBox.x < state.x)
  370. {
  371. x += (state.x - box.x) / scale;
  372. result.width = box.width;
  373. }
  374. if (state.text.boundingBox.y < state.y)
  375. {
  376. y += (state.y - box.y) / scale;
  377. result.height = box.height;
  378. }
  379. }
  380. }
  381. if (this.parent != null)
  382. {
  383. var parent = model.getParent(cell);
  384. if (parent != null && parent != this.parent)
  385. {
  386. var parentOffset = this.getParentOffset(parent);
  387. x = x - parentOffset.x;
  388. y = y - parentOffset.y;
  389. }
  390. }
  391. if (geometry.x != x || geometry.y != y)
  392. {
  393. geometry = geometry.clone();
  394. geometry.x = x;
  395. geometry.y = y;
  396. model.setGeometry(cell, geometry);
  397. }
  398. }
  399. return result;
  400. };
  401. /**
  402. * Function: getVertexBounds
  403. *
  404. * Returns an <mxRectangle> that defines the bounds of the given cell or
  405. * the bounding box if <useBoundingBox> is true.
  406. */
  407. mxGraphLayout.prototype.getVertexBounds = function(cell)
  408. {
  409. var geo = this.graph.getModel().getGeometry(cell);
  410. // Checks for oversize label bounding box and corrects
  411. // the return value accordingly
  412. // TODO: Use mxUtils.getStringSize for label bounds
  413. if (this.useBoundingBox)
  414. {
  415. var state = this.graph.getView().getState(cell);
  416. if (state != null && state.text != null && state.text.boundingBox != null)
  417. {
  418. var scale = this.graph.getView().scale;
  419. var tmp = state.text.boundingBox;
  420. var dx0 = Math.max(state.x - tmp.x, 0) / scale;
  421. var dy0 = Math.max(state.y - tmp.y, 0) / scale;
  422. var dx1 = Math.max((tmp.x + tmp.width) - (state.x + state.width), 0) / scale;
  423. var dy1 = Math.max((tmp.y + tmp.height) - (state.y + state.height), 0) / scale;
  424. geo = new mxRectangle(geo.x - dx0, geo.y - dy0, geo.width + dx0 + dx1, geo.height + dy0 + dy1);
  425. }
  426. }
  427. if (this.parent != null)
  428. {
  429. var parent = this.graph.getModel().getParent(cell);
  430. geo = geo.clone();
  431. if (parent != null && parent != this.parent)
  432. {
  433. var parentOffset = this.getParentOffset(parent);
  434. geo.x = geo.x + parentOffset.x;
  435. geo.y = geo.y + parentOffset.y;
  436. }
  437. }
  438. return new mxRectangle(geo.x, geo.y, geo.width, geo.height);
  439. };
  440. /**
  441. * Function: arrangeGroups
  442. *
  443. * Shortcut to <mxGraph.updateGroupBounds> with moveGroup set to true.
  444. */
  445. mxGraphLayout.prototype.arrangeGroups = function(cells, border, topBorder, rightBorder, bottomBorder, leftBorder)
  446. {
  447. return this.graph.updateGroupBounds(cells, border, true, topBorder, rightBorder, bottomBorder, leftBorder);
  448. };
  449. /**
  450. * Class: WeightedCellSorter
  451. *
  452. * A utility class used to track cells whilst sorting occurs on the weighted
  453. * sum of their connected edges. Does not violate (x.compareTo(y)==0) ==
  454. * (x.equals(y))
  455. *
  456. * Constructor: WeightedCellSorter
  457. *
  458. * Constructs a new weighted cell sorted for the given cell and weight.
  459. */
  460. function WeightedCellSorter(cell, weightedValue)
  461. {
  462. this.cell = cell;
  463. this.weightedValue = weightedValue;
  464. };
  465. /**
  466. * Variable: weightedValue
  467. *
  468. * The weighted value of the cell stored.
  469. */
  470. WeightedCellSorter.prototype.weightedValue = 0;
  471. /**
  472. * Variable: nudge
  473. *
  474. * Whether or not to flip equal weight values.
  475. */
  476. WeightedCellSorter.prototype.nudge = false;
  477. /**
  478. * Variable: visited
  479. *
  480. * Whether or not this cell has been visited in the current assignment.
  481. */
  482. WeightedCellSorter.prototype.visited = false;
  483. /**
  484. * Variable: rankIndex
  485. *
  486. * The index this cell is in the model rank.
  487. */
  488. WeightedCellSorter.prototype.rankIndex = null;
  489. /**
  490. * Variable: cell
  491. *
  492. * The cell whose median value is being calculated.
  493. */
  494. WeightedCellSorter.prototype.cell = null;
  495. /**
  496. * Function: compare
  497. *
  498. * Compares two WeightedCellSorters.
  499. */
  500. WeightedCellSorter.prototype.compare = function(a, b)
  501. {
  502. if (a != null && b != null)
  503. {
  504. if (b.weightedValue > a.weightedValue)
  505. {
  506. return -1;
  507. }
  508. else if (b.weightedValue < a.weightedValue)
  509. {
  510. return 1;
  511. }
  512. else
  513. {
  514. if (b.nudge)
  515. {
  516. return -1;
  517. }
  518. else
  519. {
  520. return 1;
  521. }
  522. }
  523. }
  524. else
  525. {
  526. return 0;
  527. }
  528. };
  529. __mxOutput.mxGraphLayout = typeof mxGraphLayout !== 'undefined' ? mxGraphLayout : undefined;