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