mxOrgChartLayout.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. /**
  2. * Copyright (c) 2019-2020, JGraph Ltd
  3. */
  4. /**
  5. * Class: mxOrgChartLayout
  6. *
  7. * Extends <mxGraphLayout> to implement organization chart layout algorithm.
  8. * The vertices need to be connected for this layout to work, vertices
  9. * with no connections are ignored.
  10. *
  11. * Example:
  12. *
  13. * (code)
  14. * var layout = new mxOrgChartLayout(graph);
  15. * layout.execute(graph.getDefaultParent());
  16. * (end)
  17. *
  18. */
  19. function mxOrgChartLayout(graph, branchOptimizer, parentChildSpacing, siblingSpacing)
  20. {
  21. mxGraphLayout.call(this, graph);
  22. this.correctY = false;
  23. switch(parseInt(branchOptimizer))
  24. {
  25. case 0:
  26. this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_LINEAR;
  27. this.correctY = true;
  28. break;
  29. case 1:
  30. this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_HANGER2;
  31. this.correctY = true;
  32. break;
  33. case 3:
  34. this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_FISHBONE1;
  35. break;
  36. case 4:
  37. this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_FISHBONE2;
  38. break;
  39. case 5:
  40. this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_1COLUMN_L;
  41. break;
  42. case 6:
  43. this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_1COLUMN_R;
  44. break;
  45. case 7:
  46. this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_SMART;
  47. break;
  48. default: //and case 2
  49. this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_HANGER4;
  50. this.correctY = true;
  51. }
  52. this.parentChildSpacing = parentChildSpacing > 0 ? parentChildSpacing : 20;
  53. this.siblingSpacing = siblingSpacing > 0 ? siblingSpacing : 20;
  54. };
  55. /**
  56. * Extends mxGraphLayout.
  57. */
  58. mxOrgChartLayout.prototype = new mxGraphLayout();
  59. mxOrgChartLayout.prototype.constructor = mxOrgChartLayout;
  60. //Branch Optimizers
  61. mxOrgChartLayout.prototype.BRANCH_OPT_LINEAR = 'branchOptimizerAllLinear';
  62. mxOrgChartLayout.prototype.BRANCH_OPT_HANGER2 = 'branchOptimizerAllHanger2';
  63. mxOrgChartLayout.prototype.BRANCH_OPT_HANGER4 = 'branchOptimizerAllHanger4';
  64. mxOrgChartLayout.prototype.BRANCH_OPT_FISHBONE1 = 'branchOptimizerAllFishbone1';
  65. mxOrgChartLayout.prototype.BRANCH_OPT_FISHBONE2 = 'branchOptimizerAllFishbone2';
  66. mxOrgChartLayout.prototype.BRANCH_OPT_1COLUMN_L = 'branchOptimizerAllSingleColumnLeft';
  67. mxOrgChartLayout.prototype.BRANCH_OPT_1COLUMN_R = 'branchOptimizerAllSingleColumnRight';
  68. mxOrgChartLayout.prototype.BRANCH_OPT_SMART = 'branchOptimizerSmart';
  69. /**
  70. * Function: execute
  71. *
  72. * Implements <mxGraphLayout.execute>. This operates on all children of the
  73. * given parent.
  74. */
  75. mxOrgChartLayout.prototype.execute = function(parent)
  76. {
  77. this.graph.model.beginUpdate();
  78. try
  79. {
  80. RPOrgChart.main(this.graph, parent, this.branchOptimizer, this.parentChildSpacing, this.siblingSpacing, this.correctY);
  81. }
  82. finally
  83. {
  84. this.graph.model.endUpdate();
  85. }
  86. }
  87. Bridge.define('RPOrgChart',
  88. {
  89. statics: {
  90. config: {
  91. init: function() {
  92. }
  93. },
  94. main: function (graph, parent, branchOptimizer, parentChildSpacing, siblingSpacing, correctY) {
  95. Bridge.Console.log = console.log;
  96. Bridge.Console.error = console.error;
  97. Bridge.Console.debug = console.debug;
  98. RPOrgChart.graph = graph;
  99. RPOrgChart.parent = parent;
  100. RPOrgChart.dx = 0;
  101. RPOrgChart.dy = 0;
  102. if (parent.style == 'group' && parent.geometry)
  103. {
  104. RPOrgChart.dx = parent.geometry.x;
  105. RPOrgChart.dy = parent.geometry.y;
  106. }
  107. RPOrgChart.branchOptimizer = branchOptimizer;
  108. RPOrgChart.correctY = correctY;
  109. RPOrgChart.parentChildSpacing = parseInt(parentChildSpacing);
  110. RPOrgChart.siblingSpacing = parseInt(siblingSpacing);
  111. RPOrgChart.buildChart(true);
  112. },
  113. diagram: {},
  114. dataSource: {},
  115. buildChart: function (initData) {
  116. if (initData) {
  117. RPOrgChart.initDiagram();
  118. }
  119. RPOrgChart.positionBoxes();
  120. },
  121. collapseAllBoxes: function(boxContainer, isCollapsed) {
  122. var en = boxContainer.getBoxesById().getValues().getEnumerator();
  123. while (en.moveNext()) {
  124. var box = en.getCurrent();
  125. if (!box.IsSpecial) {
  126. box.IsCollapsed = isCollapsed;
  127. }
  128. }
  129. },
  130. generateData: function ()
  131. {
  132. var dataSource = new OrgChart.Test.TestDataSource();
  133. var graph = RPOrgChart.graph;
  134. var cells = graph.getChildVertices(RPOrgChart.parent);
  135. for (var i = 0; i < cells.length; i++)
  136. {
  137. var cell = cells[i];
  138. if (cell.geometry != null && cell.vertex && cell.parent == RPOrgChart.parent) //Vertices and first level children only
  139. {
  140. // Find cell parent. If it has more than one parent, take first parent (should be an error?)
  141. var parentId = null;
  142. var incomingEdge = graph.getIncomingEdges(cell)[0];
  143. if (incomingEdge != null && incomingEdge.source != null)
  144. {
  145. parentId = incomingEdge.source.id;
  146. }
  147. var item = new OrgChart.Test.TestDataItem();
  148. item.Id = cell.id;
  149. item.ParentId = parentId;
  150. dataSource.Items.add(item.getId(), item);
  151. }
  152. }
  153. return dataSource;
  154. },
  155. initDiagram: function () {
  156. var dataSource = RPOrgChart.generateData();
  157. RPOrgChart.dataSource = dataSource;
  158. var boxContainer = new OrgChart.Layout.BoxContainer.$ctor1(dataSource);
  159. RPOrgChart.diagram = new OrgChart.Layout.Diagram();
  160. var diagram = RPOrgChart.diagram;
  161. diagram.setBoxes(boxContainer);
  162. var linearLayoutStrategy = new OrgChart.Layout.LinearLayoutStrategy();
  163. linearLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
  164. linearLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  165. linearLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  166. diagram.LayoutSettings.LayoutStrategies.add("linear", linearLayoutStrategy);
  167. var multiLineHangerLayoutStrategy = new OrgChart.Layout.MultiLineHangerLayoutStrategy();
  168. multiLineHangerLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
  169. multiLineHangerLayoutStrategy.MaxSiblingsPerRow = 2;
  170. multiLineHangerLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  171. multiLineHangerLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  172. diagram.LayoutSettings.LayoutStrategies.add("hanger2", multiLineHangerLayoutStrategy);
  173. multiLineHangerLayoutStrategy = new OrgChart.Layout.MultiLineHangerLayoutStrategy();
  174. multiLineHangerLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
  175. multiLineHangerLayoutStrategy.MaxSiblingsPerRow = 4;
  176. multiLineHangerLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  177. multiLineHangerLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  178. diagram.LayoutSettings.LayoutStrategies.add("hanger4", multiLineHangerLayoutStrategy);
  179. var singleColumnLayoutStrategy = new OrgChart.Layout.SingleColumnLayoutStrategy();
  180. singleColumnLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Right;
  181. singleColumnLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  182. singleColumnLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  183. diagram.LayoutSettings.LayoutStrategies.add("singleColumnRight", singleColumnLayoutStrategy);
  184. singleColumnLayoutStrategy = new OrgChart.Layout.SingleColumnLayoutStrategy();
  185. singleColumnLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Left;
  186. singleColumnLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  187. singleColumnLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  188. diagram.LayoutSettings.LayoutStrategies.add("singleColumnLeft", singleColumnLayoutStrategy);
  189. var fishboneLayoutStrategy = new OrgChart.Layout.MultiLineFishboneLayoutStrategy();
  190. fishboneLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
  191. fishboneLayoutStrategy.MaxGroups = 1;
  192. fishboneLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  193. fishboneLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  194. diagram.LayoutSettings.LayoutStrategies.add("fishbone1", fishboneLayoutStrategy);
  195. fishboneLayoutStrategy = new OrgChart.Layout.MultiLineFishboneLayoutStrategy();
  196. fishboneLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
  197. fishboneLayoutStrategy.MaxGroups = 2;
  198. fishboneLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  199. fishboneLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  200. diagram.LayoutSettings.LayoutStrategies.add("fishbone2", fishboneLayoutStrategy);
  201. var hstackLayoutStrategy = new OrgChart.Layout.StackingLayoutStrategy();
  202. hstackLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.InvalidValue;
  203. hstackLayoutStrategy.Orientation = OrgChart.Layout.StackOrientation.SingleRowHorizontal;
  204. hstackLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  205. hstackLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  206. diagram.LayoutSettings.LayoutStrategies.add("hstack", hstackLayoutStrategy);
  207. var vstackLayoutStrategy = new OrgChart.Layout.StackingLayoutStrategy();
  208. vstackLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.InvalidValue;
  209. vstackLayoutStrategy.Orientation = OrgChart.Layout.StackOrientation.SingleColumnVertical;
  210. vstackLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  211. vstackLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  212. diagram.LayoutSettings.LayoutStrategies.add("vstack", vstackLayoutStrategy);
  213. vstackLayoutStrategy = new OrgChart.Layout.StackingLayoutStrategy();
  214. vstackLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.InvalidValue;
  215. vstackLayoutStrategy.Orientation = OrgChart.Layout.StackOrientation.SingleColumnVertical;
  216. vstackLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  217. vstackLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  218. diagram.LayoutSettings.LayoutStrategies.add("vstackMiddle", vstackLayoutStrategy);
  219. vstackLayoutStrategy = new OrgChart.Layout.StackingLayoutStrategy();
  220. vstackLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.InvalidValue;
  221. vstackLayoutStrategy.Orientation = OrgChart.Layout.StackOrientation.SingleColumnVertical;
  222. vstackLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  223. vstackLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  224. diagram.LayoutSettings.LayoutStrategies.add("vstackTop", vstackLayoutStrategy);
  225. var assistantsLayoutStrategy = new OrgChart.Layout.FishboneAssistantsLayoutStrategy();
  226. assistantsLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
  227. assistantsLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
  228. assistantsLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
  229. diagram.LayoutSettings.LayoutStrategies.add("assistants", assistantsLayoutStrategy);
  230. diagram.LayoutSettings.DefaultLayoutStrategyId = "vstack";
  231. diagram.LayoutSettings.DefaultAssistantLayoutStrategyId = "assistants";
  232. //diagram.LayoutSettings.setBranchSpacing(5);
  233. },
  234. getBoxLevel: function(boxContainer, box) {
  235. var level = 0;
  236. var obj = {};
  237. while (box.ParentId > 0) {
  238. if (!boxContainer.getBoxesById().tryGetValue(box.ParentId, obj)) {
  239. break;
  240. }
  241. box = obj.v;
  242. level++;
  243. }
  244. return level;
  245. },
  246. onLayoutStateChanged: function (sender, args) {
  247. if (args.State.getCurrentOperation() === OrgChart.Layout.LayoutState.Operation.PreprocessVisualTree) {
  248. // When layout algorithm is ready to preprocess the tree,
  249. // we need to have box sizes ready -> hence have to render visible boxes in HTML.
  250. // Rendering can happen at earlier time, but it's just more convenient to do it here,
  251. // to utilize some readily available information about visual tree.
  252. RPOrgChart.renderBoxes();
  253. }
  254. },
  255. renderBoxes: function () {
  256. var visitorFunc = function (node) {
  257. var box = node.Element;
  258. if (box.getIsDataBound()) {
  259. // we're being run when nodes have already been marked as visible or hidden,
  260. // based on IsCollapsed attribute of each Box
  261. // so use this knowledge to prevent unnecessary rendering of invisible branches
  262. if (node.State.IsHidden) {
  263. return true;
  264. }
  265. box.Size = RPOrgChart.getBoxElementSize(box.DataId);
  266. }
  267. return true;
  268. }
  269. RPOrgChart.diagram.getVisualTree().IterateParentFirst(visitorFunc);
  270. },
  271. getBranchOptimizerFunc: function () {
  272. return RPOrgChart[RPOrgChart.branchOptimizer];
  273. },
  274. branchOptimizerAllLinear: function(node) {
  275. return node.getIsAssistantRoot() ? null : "linear";
  276. },
  277. branchOptimizerAllHanger2: function(node) {
  278. return node.getIsAssistantRoot() ? null : "hanger2";
  279. },
  280. branchOptimizerAllHanger4: function(node) {
  281. return node.getIsAssistantRoot() ? null : "hanger4";
  282. },
  283. branchOptimizerAllFishbone1: function(node) {
  284. return node.getIsAssistantRoot() ? null : "fishbone1";
  285. },
  286. branchOptimizerAllFishbone2: function (node) {
  287. return node.getIsAssistantRoot() ? null : "fishbone2";
  288. },
  289. branchOptimizerAllSingleColumnLeft: function (node) {
  290. return node.getIsAssistantRoot() ? null : "singleColumnRight";
  291. },
  292. branchOptimizerAllSingleColumnRight: function (node) {
  293. return node.getIsAssistantRoot() ? null : "singleColumnLeft";
  294. },
  295. branchOptimizerStackers: function(node) {
  296. if (node.getIsAssistantRoot()) {
  297. return null;
  298. }
  299. return node.Level === 0 // this is Node for boxContainer.SystemRoot, which is not visible itself
  300. ? "vstackTop"
  301. : node.Level === 1 // this is children of SystemRoot - they appear as roots in the diagram
  302. ? "vstackMiddle"
  303. : "hstack";
  304. },
  305. branchOptimizerSmart: function(node) {
  306. if (node.getIsAssistantRoot()) {
  307. return null;
  308. }
  309. var childCount = node.getChildCount();
  310. if (childCount <= 1) {
  311. return "vstack";
  312. }
  313. var nonLeafChildren = 0;
  314. for (var i = 0; i < childCount; i++) {
  315. if (node.Children.getItem(i).getChildCount() > 0) {
  316. nonLeafChildren++;
  317. }
  318. }
  319. if (nonLeafChildren <= 1) {
  320. if (childCount <= 4) {
  321. return "vstack";
  322. }
  323. if (childCount <= 8) {
  324. return "fishbone1";
  325. }
  326. return "fishbone2";
  327. }
  328. return "hanger4";
  329. },
  330. boxSizeFunc: function (dataId) {
  331. // ChartLayoutAlgorithm requires this function to accept data ID
  332. // so have to convert it to Box ID first, to get rendered visual element
  333. var boxId = RPOrgChart.diagram.getBoxes().getBoxesByDataId().getItem(dataId).Id;
  334. return RPOrgChart.diagram.getBoxes().getBoxesById().getItem(boxId).Size;
  335. },
  336. getBoxElementSize: function (boxId) {
  337. var geo = RPOrgChart.graph.model.cells[boxId].geometry;
  338. return new OrgChart.Layout.Size.$ctor1(geo.width, geo.height);
  339. },
  340. positionBoxes: function () {
  341. var diagram = RPOrgChart.diagram;
  342. var state = new OrgChart.Layout.LayoutState(diagram);
  343. state.addOperationChanged(RPOrgChart.onLayoutStateChanged);
  344. state.BoxSizeFunc = Bridge.fn.bind(this, RPOrgChart.boxSizeFunc, null, true);
  345. state.LayoutOptimizerFunc = Bridge.fn.bind(this, RPOrgChart.getBranchOptimizerFunc(), null, true);
  346. OrgChart.Layout.LayoutAlgorithm.Apply(state);
  347. var diagramBoundary = OrgChart.Layout.LayoutAlgorithm.ComputeBranchVisualBoundingRect(diagram.getVisualTree());
  348. var offsetx = -diagramBoundary.getLeft() + diagramBoundary.getTop();
  349. var graph = RPOrgChart.graph;
  350. var cells = graph.model.cells;
  351. var pointsList = [];
  352. var visitorVertexFunc = function (node)
  353. {
  354. if (node.State.IsHidden) {
  355. return false;
  356. }
  357. var box = node.Element;
  358. if (box.getIsDataBound()) {
  359. var cell = cells[box.DataId];
  360. var geo = cell.geometry.clone();
  361. geo.x = node.State.TopLeft.X + offsetx;
  362. geo.y = node.State.TopLeft.Y;
  363. graph.model.setGeometry(cell, geo);
  364. }
  365. return true;
  366. }
  367. var visitorEdgeFunc = function (node)
  368. {
  369. //The algorithm default is 5 px only above the node, this centers it
  370. var yCorrection = RPOrgChart.correctY? Math.min(0, -(RPOrgChart.parentChildSpacing / 2) + 5) : 0;
  371. // Render connectors
  372. if (node.State.Connector != null) {
  373. var cell = cells[node.Element.DataId];
  374. var outgoingEdge = graph.getOutgoingEdges(cell);
  375. var uniquePoints = {};
  376. //Sort segments points from top to bottom or left to right + add offset
  377. for (var ix = 0; ix < node.State.Connector.Segments.length; ix++)
  378. {
  379. var edge = node.State.Connector.Segments[ix];
  380. edge.mark = 1 << ix; //TODO Support up to 31 segments. In this a limit?
  381. edge.From.X += offsetx;
  382. edge.To.X += offsetx;
  383. var fx = edge.From.X, fy = edge.From.Y, tx = edge.To.X, ty = edge.To.Y;
  384. if ((fx == tx && fy > ty) || (fy == ty && fx > tx))
  385. {
  386. var tmp = edge.From;
  387. edge.From = edge.To;
  388. edge.To = tmp;
  389. }
  390. }
  391. //Collecting points including intersection of segments
  392. for (var ix = 0; ix < node.State.Connector.Segments.length; ix++)
  393. {
  394. var edge = node.State.Connector.Segments[ix];
  395. var fx = edge.From.X, fy = edge.From.Y, tx = edge.To.X, ty = edge.To.Y;
  396. var fp = new mxPoint(fx, fy);
  397. pointsList.push(fp);
  398. fp.mark = edge.mark;
  399. var up = uniquePoints[fx + ',' + fy];
  400. if (up != null)
  401. {
  402. up.mark |= fp.mark;
  403. }
  404. else
  405. {
  406. uniquePoints[fx + ',' + fy] = fp;
  407. }
  408. var tp = new mxPoint(tx, ty);
  409. pointsList.push(tp);
  410. tp.mark = edge.mark;
  411. var up = uniquePoints[tx + ',' + ty];
  412. if (up != null)
  413. {
  414. up.mark |= tp.mark;
  415. }
  416. else
  417. {
  418. uniquePoints[tx + ',' + ty] = tp;
  419. }
  420. //Find intersections
  421. for (var j = ix + 1; j < node.State.Connector.Segments.length; j++)
  422. {
  423. var e2 = node.State.Connector.Segments[j];
  424. var fx2 = e2.From.X, fy2 = e2.From.Y, tx2 = e2.To.X, ty2 = e2.To.Y;
  425. if (fx == tx && fy <= fy2 && ty >= fy2 && fx2 <= fx && tx2 >= fx) //Ver |_ Hor
  426. {
  427. var ip = new mxPoint(fx, fy2);
  428. pointsList.push(ip);
  429. ip.mark = edge.mark | e2.mark;
  430. var up = uniquePoints[fx + ',' + fy2];
  431. if (up != null)
  432. {
  433. up.mark |= ip.mark;
  434. }
  435. else
  436. {
  437. uniquePoints[fx + ',' + fy2] = ip;
  438. }
  439. }
  440. else if (fy == ty && fx <= fx2 && tx >= fx2 && fy2 <= fy && ty2 >= fy) //Hor _| Ver
  441. {
  442. var ip = new mxPoint(fx2, fy);
  443. pointsList.push(ip);
  444. ip.mark = edge.mark | e2.mark;
  445. var up = uniquePoints[fx2 + ',' + fy]
  446. if (up != null)
  447. {
  448. up.mark |= ip.mark;
  449. }
  450. else
  451. {
  452. uniquePoints[fx2 + ',' + fy] = ip;
  453. }
  454. }
  455. }
  456. }
  457. //Sort points on y then x
  458. var pointsArr = [];
  459. for (var k in uniquePoints)
  460. {
  461. pointsArr.push(uniquePoints[k]);
  462. }
  463. pointsArr.sort(function(a, b)
  464. {
  465. var dy = a.y - b.y;
  466. return dy == 0? a.x - b.x : dy;
  467. });
  468. function pointOnCell(geo, p)
  469. {
  470. return p.x >= geo.x && p.x <= geo.x + geo.width && p.y >= geo.y && p.y <= geo.y + geo.height;
  471. };
  472. function adjustEdgeGeoAndStyle(edge, edgePoints)
  473. {
  474. var eGeo = edge.geometry.clone();
  475. for (var i = 0; edgePoints && i < edgePoints.length; i++)
  476. {
  477. if (!edgePoints[i].corrected)
  478. {
  479. edgePoints[i].y += yCorrection;
  480. edgePoints[i].corrected = true
  481. }
  482. }
  483. eGeo.points = edgePoints;
  484. graph.model.setGeometry(edge, eGeo);
  485. //Remove entry and exit points
  486. graph.setCellStyles('entryX', null, [edge]);
  487. graph.setCellStyles('entryY', null, [edge]);
  488. graph.setCellStyles('exitX', null, [edge]);
  489. graph.setCellStyles('exitY', null, [edge]);
  490. //Set type orthogonal
  491. graph.setCellStyles('edgeStyle', 'orthogonalEdgeStyle', [edge]);
  492. };
  493. var outgoingEdge = graph.getOutgoingEdges(cell);
  494. //Simple case of a single segment. TODO Handle this case earlier
  495. if (pointsArr.length == 2 && outgoingEdge.length == 1)
  496. {
  497. adjustEdgeGeoAndStyle(outgoingEdge[0], pointsArr);
  498. }
  499. else
  500. {
  501. var srcGeo = cell.geometry;
  502. var srcP;
  503. //Find src starting point //TODO It should be first point always?
  504. for (var i = 0; i < pointsArr.length; i++)
  505. {
  506. if (pointOnCell(srcGeo, pointsArr[i]))
  507. {
  508. srcP = pointsArr[i];
  509. break;
  510. }
  511. }
  512. var selected;
  513. function getNextPoint(lp)
  514. {
  515. for (var i = 0; i < pointsArr.length; i++)
  516. {
  517. var p = pointsArr[i];
  518. if (selected[p.x + ',' + p.y]) continue;
  519. if (p.mark & lp.mark)
  520. {
  521. selected[p.x + ',' + p.y] = true;
  522. return p;
  523. }
  524. }
  525. }
  526. for (var j = 0; j < outgoingEdge.length; j++)
  527. {
  528. if (outgoingEdge[j].target != null)
  529. {
  530. selected = {};
  531. selected[srcP.x + ',' + srcP.y] = true;
  532. var trgGeo = outgoingEdge[j].target.geometry;
  533. var edgePoints = [srcP];
  534. var lp = srcP;
  535. var safeGuard = 0;
  536. //Is BFS better?
  537. while (safeGuard < 1000)
  538. {
  539. safeGuard++;
  540. var np = getNextPoint(lp);
  541. //retract, then remove this point
  542. if (np == null)
  543. {
  544. edgePoints.pop();
  545. lp = edgePoints[edgePoints.length - 1];
  546. }
  547. else
  548. {
  549. edgePoints.push(np);
  550. lp = np;
  551. if (pointOnCell(trgGeo, np)) break;
  552. }
  553. }
  554. //Remove retracted points TODO can we do it in a better way?
  555. if (edgePoints.length > 2)
  556. {
  557. var spX = edgePoints[0].x;
  558. var lpX = edgePoints[edgePoints.length - 1].x;
  559. for (var i = edgePoints.length - 2; i > 0; i--)
  560. {
  561. if ((spX > lpX && edgePoints[i].x < lpX) || (spX < lpX && edgePoints[i].x < spX))
  562. {
  563. edgePoints.splice(i, 1);
  564. }
  565. }
  566. }
  567. var eGeo = outgoingEdge[j].geometry.clone();
  568. eGeo.points = edgePoints;
  569. RPOrgChart.graph.model.setGeometry(outgoingEdge[j], eGeo);
  570. //Fix edge points and style
  571. adjustEdgeGeoAndStyle(outgoingEdge[j], edgePoints);
  572. }
  573. }
  574. }
  575. }
  576. return true;
  577. }
  578. diagram.getVisualTree().IterateParentFirst(visitorVertexFunc);
  579. diagram.getVisualTree().IterateParentFirst(visitorEdgeFunc);
  580. //Cleanup
  581. for (var i = 0; i < pointsList.length; i++)
  582. {
  583. delete pointsList[i].mark;
  584. delete pointsList[i].corrected;
  585. }
  586. }
  587. }
  588. });
  589. Bridge.init();