1/* 2 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24package com.sun.hotspot.igv.hierarchicallayout; 25 26import com.sun.hotspot.igv.layout.LayoutGraph; 27import com.sun.hotspot.igv.layout.LayoutManager; 28import com.sun.hotspot.igv.layout.Link; 29import com.sun.hotspot.igv.layout.Vertex; 30import java.awt.Dimension; 31import java.awt.Point; 32import java.util.*; 33 34/** 35 * 36 * @author Thomas Wuerthinger 37 */ 38public class HierarchicalLayoutManager implements LayoutManager { 39 40 public static final boolean TRACE = false; 41 public static final boolean CHECK = false; 42 public static final int SWEEP_ITERATIONS = 1; 43 public static final int CROSSING_ITERATIONS = 2; 44 public static final int DUMMY_HEIGHT = 1; 45 public static final int DUMMY_WIDTH = 1; 46 public static final int X_OFFSET = 8; 47 public static final int LAYER_OFFSET = 8; 48 public static final int MAX_LAYER_LENGTH = -1; 49 public static final int MIN_LAYER_DIFFERENCE = 1; 50 public static final int VIP_BONUS = 10; 51 52 public enum Combine { 53 54 NONE, 55 SAME_INPUTS, 56 SAME_OUTPUTS 57 } 58 // Options 59 private Combine combine; 60 private int dummyWidth; 61 private int dummyHeight; 62 private int xOffset; 63 private int layerOffset; 64 private int maxLayerLength; 65 private int minLayerDifference; 66 // Algorithm global datastructures 67 private Set<Link> reversedLinks; 68 private List<LayoutNode> nodes; 69 private HashMap<Vertex, LayoutNode> vertexToLayoutNode; 70 private HashMap<Link, List<Point>> reversedLinkStartPoints; 71 private HashMap<Link, List<Point>> reversedLinkEndPoints; 72 private HashMap<LayoutEdge, LayoutEdge> bottomEdgeHash; 73 private HashMap<Link, List<Point>> splitStartPoints; 74 private HashMap<Link, List<Point>> splitEndPoints; 75 private LayoutGraph graph; 76 private List<LayoutNode>[] layers; 77 private int layerCount; 78 private Set<? extends Link> importantLinks; 79 private Set<Link> linksToFollow; 80 81 private class LayoutNode { 82 83 public int x; 84 public int y; 85 public int width; 86 public int height; 87 public int layer = -1; 88 public int xOffset; 89 public int yOffset; 90 public int bottomYOffset; 91 public Vertex vertex; // Only used for non-dummy nodes, otherwise null 92 93 public List<LayoutEdge> preds = new ArrayList<>(); 94 public List<LayoutEdge> succs = new ArrayList<>(); 95 public HashMap<Integer, Integer> outOffsets = new HashMap<>(); 96 public HashMap<Integer, Integer> inOffsets = new HashMap<>(); 97 public int pos = -1; // Position within layer 98 99 public int crossingNumber; 100 101 @Override 102 public String toString() { 103 return "Node " + vertex; 104 } 105 } 106 107 private class LayoutEdge { 108 109 public LayoutNode from; 110 public LayoutNode to; 111 public int relativeFrom; 112 public int relativeTo; 113 public Link link; 114 public boolean vip; 115 } 116 117 private abstract class AlgorithmPart { 118 119 public void start() { 120 if (CHECK) { 121 preCheck(); 122 } 123 124 long start = 0; 125 if (TRACE) { 126 System.out.println("##################################################"); 127 System.out.println("Starting part " + this.getClass().getName()); 128 start = System.currentTimeMillis(); 129 } 130 run(); 131 if (TRACE) { 132 System.out.println("Timing for " + this.getClass().getName() + " is " + (System.currentTimeMillis() - start)); 133 printStatistics(); 134 } 135 136 if (CHECK) { 137 postCheck(); 138 } 139 } 140 141 protected abstract void run(); 142 143 protected void printStatistics() { 144 } 145 146 protected void postCheck() { 147 } 148 149 protected void preCheck() { 150 } 151 } 152 153 public HierarchicalLayoutManager() { 154 this(Combine.NONE); 155 } 156 157 public HierarchicalLayoutManager(Combine b) { 158 this.combine = b; 159 this.dummyWidth = DUMMY_WIDTH; 160 this.dummyHeight = DUMMY_HEIGHT; 161 this.xOffset = X_OFFSET; 162 this.layerOffset = LAYER_OFFSET; 163 this.maxLayerLength = MAX_LAYER_LENGTH; 164 this.minLayerDifference = MIN_LAYER_DIFFERENCE; 165 this.linksToFollow = new HashSet<>(); 166 } 167 168 public int getMaxLayerLength() { 169 return maxLayerLength; 170 } 171 172 public void setMaxLayerLength(int v) { 173 maxLayerLength = v; 174 } 175 176 public void setMinLayerDifference(int v) { 177 minLayerDifference = v; 178 } 179 180 @Override 181 public void doLayout(LayoutGraph graph) { 182 doLayout(graph, new HashSet<Link>()); 183 184 } 185 186 @Override 187 public void doLayout(LayoutGraph graph, Set<? extends Link> importantLinks) { 188 189 this.importantLinks = importantLinks; 190 this.graph = graph; 191 192 vertexToLayoutNode = new HashMap<>(); 193 reversedLinks = new HashSet<>(); 194 reversedLinkStartPoints = new HashMap<>(); 195 reversedLinkEndPoints = new HashMap<>(); 196 bottomEdgeHash = new HashMap<>(); 197 nodes = new ArrayList<>(); 198 splitStartPoints = new HashMap<>(); 199 splitEndPoints = new HashMap<>(); 200 201 // ############################################################# 202 // Step 1: Build up data structure 203 new BuildDatastructure().start(); 204 205 // ############################################################# 206 // STEP 2: Reverse edges, handle backedges 207 new ReverseEdges().start(); 208 209 for (LayoutNode n : nodes) { 210 ArrayList<LayoutEdge> tmpArr = new ArrayList<>(); 211 for (LayoutEdge e : n.succs) { 212 if (importantLinks.contains(e.link)) { 213 tmpArr.add(e); 214 } 215 } 216 217 for (LayoutEdge e : tmpArr) { 218 e.from.succs.remove(e); 219 e.to.preds.remove(e); 220 } 221 } 222 223 // ############################################################# 224 // STEP 3: Assign layers 225 new AssignLayers().start(); 226 227 // ############################################################# 228 // STEP 4: Create dummy nodes 229 new CreateDummyNodes().start(); 230 231 // ############################################################# 232 // STEP 5: Crossing Reduction 233 new CrossingReduction().start(); 234 235 // ############################################################# 236 // STEP 7: Assign X coordinates 237 new AssignXCoordinates().start(); 238 239 // ############################################################# 240 // STEP 6: Assign Y coordinates 241 new AssignYCoordinates().start(); 242 243 // ############################################################# 244 // STEP 8: Write back to interface 245 new WriteResult().start(); 246 } 247 248 private class WriteResult extends AlgorithmPart { 249 250 private int pointCount; 251 252 @Override 253 protected void run() { 254 255 HashMap<Vertex, Point> vertexPositions = new HashMap<>(); 256 HashMap<Link, List<Point>> linkPositions = new HashMap<>(); 257 for (Vertex v : graph.getVertices()) { 258 LayoutNode n = vertexToLayoutNode.get(v); 259 assert !vertexPositions.containsKey(v); 260 vertexPositions.put(v, new Point(n.x + n.xOffset, n.y + n.yOffset)); 261 } 262 263 for (LayoutNode n : nodes) { 264 265 for (LayoutEdge e : n.preds) { 266 if (e.link != null) { 267 ArrayList<Point> points = new ArrayList<>(); 268 269 Point p = new Point(e.to.x + e.relativeTo, e.to.y + e.to.yOffset + e.link.getTo().getRelativePosition().y); 270 points.add(p); 271 if (e.to.inOffsets.containsKey(e.relativeTo)) { 272 points.add(new Point(p.x, p.y + e.to.inOffsets.get(e.relativeTo) + e.link.getTo().getRelativePosition().y)); 273 } 274 275 LayoutNode cur = e.from; 276 LayoutNode other = e.to; 277 LayoutEdge curEdge = e; 278 while (cur.vertex == null && cur.preds.size() != 0) { 279 if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) { 280 points.remove(points.size() - 1); 281 } 282 points.add(new Point(cur.x + cur.width / 2, cur.y + cur.height)); 283 if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) { 284 points.remove(points.size() - 1); 285 } 286 points.add(new Point(cur.x + cur.width / 2, cur.y)); 287 assert cur.preds.size() == 1; 288 curEdge = cur.preds.get(0); 289 cur = curEdge.from; 290 } 291 292 p = new Point(cur.x + curEdge.relativeFrom, cur.y + cur.height - cur.bottomYOffset + (curEdge.link == null ? 0 : curEdge.link.getFrom().getRelativePosition().y)); 293 if (curEdge.from.outOffsets.containsKey(curEdge.relativeFrom)) { 294 points.add(new Point(p.x, p.y + curEdge.from.outOffsets.get(curEdge.relativeFrom) + (curEdge.link == null ? 0 : curEdge.link.getFrom().getRelativePosition().y))); 295 } 296 points.add(p); 297 298 Collections.reverse(points); 299 300 if (cur.vertex == null && cur.preds.size() == 0) { 301 302 if (reversedLinkEndPoints.containsKey(e.link)) { 303 for (Point p1 : reversedLinkEndPoints.get(e.link)) { 304 points.add(new Point(p1.x + e.to.x, p1.y + e.to.y)); 305 } 306 } 307 308 if (splitStartPoints.containsKey(e.link)) { 309 points.add(0, null); 310 points.addAll(0, splitStartPoints.get(e.link)); 311 312 //checkPoints(points); 313 if (reversedLinks.contains(e.link)) { 314 Collections.reverse(points); 315 } 316 assert !linkPositions.containsKey(e.link); 317 linkPositions.put(e.link, points); 318 } else { 319 splitEndPoints.put(e.link, points); 320 } 321 322 } else { 323 if (reversedLinks.contains(e.link)) { 324 Collections.reverse(points); 325 } 326 if (reversedLinkStartPoints.containsKey(e.link)) { 327 for (Point p1 : reversedLinkStartPoints.get(e.link)) { 328 points.add(new Point(p1.x + cur.x, p1.y + cur.y)); 329 } 330 } 331 332 if (reversedLinkEndPoints.containsKey(e.link)) { 333 for (Point p1 : reversedLinkEndPoints.get(e.link)) { 334 points.add(0, new Point(p1.x + other.x, p1.y + other.y)); 335 } 336 } 337 338 assert !linkPositions.containsKey(e.link); 339 linkPositions.put(e.link, points); 340 } 341 pointCount += points.size(); 342 343 // No longer needed! 344 e.link = null; 345 } 346 } 347 348 for (LayoutEdge e : n.succs) { 349 if (e.link != null) { 350 ArrayList<Point> points = new ArrayList<>(); 351 Point p = new Point(e.from.x + e.relativeFrom, e.from.y + e.from.height - e.from.bottomYOffset + e.link.getFrom().getRelativePosition().y); 352 points.add(p); 353 if (e.from.outOffsets.containsKey(e.relativeFrom)) { 354 points.add(new Point(p.x, p.y + e.from.outOffsets.get(e.relativeFrom) + e.link.getFrom().getRelativePosition().y)); 355 } 356 357 LayoutNode cur = e.to; 358 LayoutNode other = e.from; 359 LayoutEdge curEdge = e; 360 while (cur.vertex == null && !cur.succs.isEmpty()) { 361 if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) { 362 points.remove(points.size() - 1); 363 } 364 points.add(new Point(cur.x + cur.width / 2, cur.y)); 365 if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) { 366 points.remove(points.size() - 1); 367 } 368 points.add(new Point(cur.x + cur.width / 2, cur.y + cur.height)); 369 if (cur.succs.isEmpty()) { 370 break; 371 } 372 assert cur.succs.size() == 1; 373 curEdge = cur.succs.get(0); 374 cur = curEdge.to; 375 } 376 377 p = new Point(cur.x + curEdge.relativeTo, cur.y + cur.yOffset + ((curEdge.link == null) ? 0 : curEdge.link.getTo().getRelativePosition().y)); 378 points.add(p); 379 if (curEdge.to.inOffsets.containsKey(curEdge.relativeTo)) { 380 points.add(new Point(p.x, p.y + curEdge.to.inOffsets.get(curEdge.relativeTo) + ((curEdge.link == null) ? 0 : curEdge.link.getTo().getRelativePosition().y))); 381 } 382 383 if (cur.succs.isEmpty() && cur.vertex == null) { 384 if (reversedLinkStartPoints.containsKey(e.link)) { 385 for (Point p1 : reversedLinkStartPoints.get(e.link)) { 386 points.add(0, new Point(p1.x + other.x, p1.y + other.y)); 387 } 388 } 389 390 if (splitEndPoints.containsKey(e.link)) { 391 points.add(null); 392 points.addAll(splitEndPoints.get(e.link)); 393 394 //checkPoints(points); 395 if (reversedLinks.contains(e.link)) { 396 Collections.reverse(points); 397 } 398 assert !linkPositions.containsKey(e.link); 399 linkPositions.put(e.link, points); 400 } else { 401 splitStartPoints.put(e.link, points); 402 } 403 } else { 404 405 if (reversedLinkStartPoints.containsKey(e.link)) { 406 for (Point p1 : reversedLinkStartPoints.get(e.link)) { 407 points.add(0, new Point(p1.x + other.x, p1.y + other.y)); 408 } 409 } 410 if (reversedLinkEndPoints.containsKey(e.link)) { 411 for (Point p1 : reversedLinkEndPoints.get(e.link)) { 412 points.add(new Point(p1.x + cur.x, p1.y + cur.y)); 413 } 414 } 415 if (reversedLinks.contains(e.link)) { 416 Collections.reverse(points); 417 } 418 //checkPoints(points); 419 assert !linkPositions.containsKey(e.link); 420 linkPositions.put(e.link, points); 421 } 422 423 pointCount += points.size(); 424 e.link = null; 425 } 426 } 427 } 428 429 int minX = Integer.MAX_VALUE; 430 int minY = Integer.MAX_VALUE; 431 for (Vertex v : vertexPositions.keySet()) { 432 Point p = vertexPositions.get(v); 433 minX = Math.min(minX, p.x); 434 minY = Math.min(minY, p.y); 435 } 436 437 for (Link l : linkPositions.keySet()) { 438 List<Point> points = linkPositions.get(l); 439 for (Point p : points) { 440 if (p != null) { 441 minX = Math.min(minX, p.x); 442 minY = Math.min(minY, p.y); 443 } 444 } 445 446 } 447 448 for (Vertex v : vertexPositions.keySet()) { 449 Point p = vertexPositions.get(v); 450 p.x -= minX; 451 p.y -= minY; 452 v.setPosition(p); 453 } 454 455 for (Link l : linkPositions.keySet()) { 456 List<Point> points = linkPositions.get(l); 457 for (Point p : points) { 458 if (p != null) { 459 p.x -= minX; 460 p.y -= minY; 461 } 462 } 463 l.setControlPoints(points); 464 465 } 466 } 467 468 @Override 469 protected void printStatistics() { 470 System.out.println("Number of nodes: " + nodes.size()); 471 int edgeCount = 0; 472 for (LayoutNode n : nodes) { 473 edgeCount += n.succs.size(); 474 } 475 System.out.println("Number of edges: " + edgeCount); 476 System.out.println("Number of points: " + pointCount); 477 } 478 } 479 480 private static class Segment { 481 482 public float d; 483 public int orderNumber = -1; 484 public ArrayList<LayoutNode> nodes = new ArrayList<>(); 485 public HashSet<Segment> succs = new HashSet<>(); 486 public HashSet<Segment> preds = new HashSet<>(); 487 public Region region; 488 } 489 private static final Comparator<Segment> segmentComparator = new Comparator<Segment>() { 490 491 @Override 492 public int compare(Segment s1, Segment s2) { 493 return s1.orderNumber - s2.orderNumber; 494 } 495 }; 496 497 private static class Region { 498 499 public float d; 500 public int minOrderNumber; 501 public SortedSet<Segment> segments = new TreeSet<>(segmentComparator); 502 public HashSet<Region> succs = new HashSet<>(4); 503 public HashSet<Region> preds = new HashSet<>(4); 504 } 505 private static final Comparator<Region> regionComparator = new Comparator<Region>() { 506 507 @Override 508 public int compare(Region r1, Region r2) { 509 return r1.minOrderNumber - r2.minOrderNumber; 510 } 511 }; 512 private static final Comparator<LayoutNode> nodePositionComparator = new Comparator<LayoutNode>() { 513 514 @Override 515 public int compare(LayoutNode n1, LayoutNode n2) { 516 return n1.pos - n2.pos; 517 } 518 }; 519 private static final Comparator<LayoutNode> nodeProcessingDownComparator = new Comparator<LayoutNode>() { 520 @Override 521 public int compare(LayoutNode n1, LayoutNode n2) { 522 int n1VIP = 0; 523 for (LayoutEdge e : n1.preds) { 524 if (e.vip) { 525 n1VIP++; 526 } 527 } 528 int n2VIP = 0; 529 for (LayoutEdge e : n2.preds) { 530 if (e.vip) { 531 n2VIP++; 532 } 533 } 534 if (n1VIP != n2VIP) { 535 return n2VIP - n1VIP; 536 } 537 if (n1.vertex == null) { 538 if (n2.vertex == null) { 539 return 0; 540 } 541 return -1; 542 } 543 if (n2.vertex == null) { 544 return 1; 545 } 546 return n1.preds.size() - n2.preds.size(); 547 } 548 }; 549 private static final Comparator<LayoutNode> nodeProcessingUpComparator = new Comparator<LayoutNode>() { 550 551 @Override 552 public int compare(LayoutNode n1, LayoutNode n2) { 553 int n1VIP = 0; 554 for (LayoutEdge e : n1.succs) { 555 if (e.vip) { 556 n1VIP++; 557 } 558 } 559 int n2VIP = 0; 560 for (LayoutEdge e : n2.succs) { 561 if (e.vip) { 562 n2VIP++; 563 } 564 } 565 if (n1VIP != n2VIP) { 566 return n2VIP - n1VIP; 567 } 568 if (n1.vertex == null) { 569 if (n2.vertex == null) { 570 return 0; 571 } 572 return -1; 573 } 574 if (n2.vertex == null) { 575 return 1; 576 } 577 return n1.succs.size() - n2.succs.size(); 578 } 579 }; 580 581 private class AssignXCoordinates extends AlgorithmPart { 582 583 private ArrayList<Integer>[] space; 584 private ArrayList<LayoutNode>[] downProcessingOrder; 585 private ArrayList<LayoutNode>[] upProcessingOrder; 586 587 private void initialPositions() { 588 for (LayoutNode n : nodes) { 589 n.x = space[n.layer].get(n.pos); 590 } 591 } 592 593 @SuppressWarnings("unchecked") 594 private void createArrays() { 595 space = new ArrayList[layers.length]; 596 downProcessingOrder = new ArrayList[layers.length]; 597 upProcessingOrder = new ArrayList[layers.length]; 598 } 599 600 @Override 601 protected void run() { 602 createArrays(); 603 604 for (int i = 0; i < layers.length; i++) { 605 space[i] = new ArrayList<>(); 606 downProcessingOrder[i] = new ArrayList<>(); 607 upProcessingOrder[i] = new ArrayList<>(); 608 609 int curX = 0; 610 for (LayoutNode n : layers[i]) { 611 space[i].add(curX); 612 curX += n.width + xOffset; 613 downProcessingOrder[i].add(n); 614 upProcessingOrder[i].add(n); 615 } 616 617 Collections.sort(downProcessingOrder[i], nodeProcessingDownComparator); 618 Collections.sort(upProcessingOrder[i], nodeProcessingUpComparator); 619 } 620 621 initialPositions(); 622 for (int i = 0; i < SWEEP_ITERATIONS; i++) { 623 sweepDown(); 624 adjustSpace(); 625 sweepUp(false); 626 adjustSpace(); 627 } 628 629 sweepDown(); 630 adjustSpace(); 631 sweepUp(true); 632 } 633 634 private void adjustSpace() { 635 for (int i = 0; i < layers.length; i++) { 636 // space[i] = new ArrayList<>(); 637 int curX = 0; 638 for (LayoutNode n : layers[i]) { 639 space[i].add(n.x); 640// curX += n.width + xOffset; 641 } 642 } 643 } 644 645 private int calculateOptimalDown(LayoutNode n) { 646 int size = n.preds.size(); 647 if (size == 0) { 648 return n.x; 649 } 650 int vipCount = 0; 651 for (LayoutEdge e : n.preds) { 652 if (e.vip) { 653 vipCount++; 654 } 655 } 656 657 if (vipCount == 0) { 658 int[] values = new int[size]; 659 for (int i = 0; i < size; i++) { 660 LayoutEdge e = n.preds.get(i); 661 values[i] = e.from.x + e.relativeFrom - e.relativeTo; 662 } 663 return median(values); 664 } else { 665 int z = 0; 666 int[] values = new int[vipCount]; 667 for (int i = 0; i < size; i++) { 668 LayoutEdge e = n.preds.get(i); 669 if (e.vip) { 670 values[z++] = e.from.x + e.relativeFrom - e.relativeTo; 671 } 672 } 673 return median(values); 674 } 675 } 676 677 private int calculateOptimalBoth(LayoutNode n) { 678 if (n.preds.size() == n.succs.size()) { 679 return n.x; 680 } 681 682 int[] values = new int[n.preds.size() + n.succs.size()]; 683 int i = 0; 684 685 for (LayoutEdge e : n.preds) { 686 values[i] = e.from.x + e.relativeFrom - e.relativeTo; 687 i++; 688 } 689 690 for (LayoutEdge e : n.succs) { 691 values[i] = e.to.x + e.relativeTo - e.relativeFrom; 692 i++; 693 } 694 695 return median(values); 696 } 697 698 private int calculateOptimalUp(LayoutNode n) { 699 int size = n.succs.size(); 700 if (size == 0) { 701 return n.x; 702 } 703 int[] values = new int[size]; 704 for (int i = 0; i < size; i++) { 705 LayoutEdge e = n.succs.get(i); 706 values[i] = e.to.x + e.relativeTo - e.relativeFrom; 707 if (e.vip) { 708 return values[i]; 709 } 710 } 711 return median(values); 712 } 713 714 private int median(int[] values) { 715 Arrays.sort(values); 716 if (values.length % 2 == 0) { 717 return (values[values.length / 2 - 1] + values[values.length / 2]) / 2; 718 } else { 719 return values[values.length / 2]; 720 } 721 } 722 723 private void sweepUp(boolean onlyDummies) { 724 for (int i = layers.length - 1; i >= 0; i--) { 725 NodeRow r = new NodeRow(space[i]); 726 for (LayoutNode n : upProcessingOrder[i]) { 727 int optimal = calculateOptimalUp(n); 728 r.insert(n, optimal); 729 } 730 } 731 } 732 733 private void doubleSweep() { 734 for (int i = layers.length - 2; i >= 0; i--) { 735 NodeRow r = new NodeRow(space[i]); 736 for (LayoutNode n : upProcessingOrder[i]) { 737 int optimal = calculateOptimalBoth(n); 738 r.insert(n, optimal); 739 } 740 } 741 } 742 743 private void sweepDown() { 744 for (int i = 1; i < layers.length; i++) { 745 NodeRow r = new NodeRow(space[i]); 746 for (LayoutNode n : downProcessingOrder[i]) { 747 int optimal = calculateOptimalDown(n); 748 r.insert(n, optimal); 749 } 750 } 751 } 752 } 753 754 private static class NodeRow { 755 756 private TreeSet<LayoutNode> treeSet; 757 private ArrayList<Integer> space; 758 759 public NodeRow(ArrayList<Integer> space) { 760 treeSet = new TreeSet<>(nodePositionComparator); 761 this.space = space; 762 } 763 764 public int offset(LayoutNode n1, LayoutNode n2) { 765 int v1 = space.get(n1.pos) + n1.width; 766 int v2 = space.get(n2.pos); 767 return v2 - v1; 768 } 769 770 public void insert(LayoutNode n, int pos) { 771 772 SortedSet<LayoutNode> headSet = treeSet.headSet(n); 773 774 LayoutNode leftNeighbor = null; 775 int minX = Integer.MIN_VALUE; 776 if (!headSet.isEmpty()) { 777 leftNeighbor = headSet.last(); 778 minX = leftNeighbor.x + leftNeighbor.width + offset(leftNeighbor, n); 779 } 780 781 if (pos < minX) { 782 n.x = minX; 783 } else { 784 785 LayoutNode rightNeighbor = null; 786 SortedSet<LayoutNode> tailSet = treeSet.tailSet(n); 787 int maxX = Integer.MAX_VALUE; 788 if (!tailSet.isEmpty()) { 789 rightNeighbor = tailSet.first(); 790 maxX = rightNeighbor.x - offset(n, rightNeighbor) - n.width; 791 } 792 793 if (pos > maxX) { 794 n.x = maxX; 795 } else { 796 n.x = pos; 797 } 798 799 assert minX <= maxX : minX + " vs " + maxX; 800 } 801 802 treeSet.add(n); 803 } 804 } 805 private static Comparator<LayoutNode> crossingNodeComparator = new Comparator<LayoutNode>() { 806 807 @Override 808 public int compare(LayoutNode n1, LayoutNode n2) { 809 return n1.crossingNumber - n2.crossingNumber; 810 } 811 }; 812 813 private class CrossingReduction extends AlgorithmPart { 814 815 @Override 816 public void preCheck() { 817 for (LayoutNode n : nodes) { 818 assert n.layer < layerCount; 819 } 820 } 821 822 @SuppressWarnings("unchecked") 823 private void createLayers() { 824 layers = new List[layerCount]; 825 826 for (int i = 0; i < layerCount; i++) { 827 layers[i] = new ArrayList<>(); 828 } 829 } 830 831 @Override 832 protected void run() { 833 createLayers(); 834 835 // Generate initial ordering 836 HashSet<LayoutNode> visited = new HashSet<>(); 837 for (LayoutNode n : nodes) { 838 if (n.layer == 0) { 839 layers[0].add(n); 840 visited.add(n); 841 } else if (n.preds.isEmpty()) { 842 layers[n.layer].add(n); 843 visited.add(n); 844 } 845 } 846 847 for (int i = 0; i < layers.length - 1; i++) { 848 for (LayoutNode n : layers[i]) { 849 for (LayoutEdge e : n.succs) { 850 if (!visited.contains(e.to)) { 851 visited.add(e.to); 852 layers[i + 1].add(e.to); 853 } 854 } 855 } 856 } 857 858 updatePositions(); 859 860 initX(); 861 862 // Optimize 863 for (int i = 0; i < CROSSING_ITERATIONS; i++) { 864 downSweep(); 865 upSweep(); 866 } 867 downSweep(); 868 } 869 870 private void initX() { 871 872 for (int i = 0; i < layers.length; i++) { 873 updateXOfLayer(i); 874 } 875 } 876 877 private void updateXOfLayer(int index) { 878 int x = 0; 879 880 for (LayoutNode n : layers[index]) { 881 n.x = x; 882 x += n.width + X_OFFSET; 883 } 884 } 885 886 private void updatePositions() { 887 888 for (int i = 0; i < layers.length; i++) { 889 int z = 0; 890 for (LayoutNode n : layers[i]) { 891 n.pos = z; 892 z++; 893 } 894 } 895 } 896 897 private void downSweep() { 898 899 // Downsweep 900 for (int i = 1; i < layerCount; i++) { 901 902 for (LayoutNode n : layers[i]) { 903 n.crossingNumber = 0; 904 } 905 906 for (LayoutNode n : layers[i]) { 907 908 int sum = 0; 909 int count = 0; 910 for (LayoutEdge e : n.preds) { 911 int cur = e.from.x + e.relativeFrom; 912 int factor = 1; 913 if (e.vip) { 914 factor = VIP_BONUS; 915 } 916 sum += cur * factor; 917 count += factor; 918 } 919 920 if (count > 0) { 921 sum /= count; 922 n.crossingNumber = sum; 923 } 924 } 925 926 updateCrossingNumbers(i, true); 927 Collections.sort(layers[i], crossingNodeComparator); 928 updateXOfLayer(i); 929 930 int z = 0; 931 for (LayoutNode n : layers[i]) { 932 n.pos = z; 933 z++; 934 } 935 } 936 } 937 938 private void updateCrossingNumbers(int index, boolean down) { 939 for (int i = 0; i < layers[index].size(); i++) { 940 LayoutNode n = layers[index].get(i); 941 LayoutNode prev = null; 942 if (i > 0) { 943 prev = layers[index].get(i - 1); 944 } 945 LayoutNode next = null; 946 if (i < layers[index].size() - 1) { 947 next = layers[index].get(i + 1); 948 } 949 950 boolean cond = n.succs.isEmpty(); 951 if (down) { 952 cond = n.preds.isEmpty(); 953 } 954 955 if (cond) { 956 957 if (prev != null && next != null) { 958 n.crossingNumber = (prev.crossingNumber + next.crossingNumber) / 2; 959 } else if (prev != null) { 960 n.crossingNumber = prev.crossingNumber; 961 } else if (next != null) { 962 n.crossingNumber = next.crossingNumber; 963 } 964 } 965 } 966 } 967 968 private void upSweep() { 969 // Upsweep 970 for (int i = layerCount - 2; i >= 0; i--) { 971 972 for (LayoutNode n : layers[i]) { 973 n.crossingNumber = 0; 974 } 975 976 for (LayoutNode n : layers[i]) { 977 978 int count = 0; 979 int sum = 0; 980 for (LayoutEdge e : n.succs) { 981 int cur = e.to.x + e.relativeTo; 982 int factor = 1; 983 if (e.vip) { 984 factor = VIP_BONUS; 985 } 986 sum += cur * factor; 987 count += factor; 988 } 989 990 if (count > 0) { 991 sum /= count; 992 n.crossingNumber = sum; 993 } 994 995 } 996 997 updateCrossingNumbers(i, false); 998 Collections.sort(layers[i], crossingNodeComparator); 999 updateXOfLayer(i); 1000 1001 int z = 0; 1002 for (LayoutNode n : layers[i]) { 1003 n.pos = z; 1004 z++; 1005 } 1006 } 1007 } 1008 1009 @Override 1010 public void postCheck() { 1011 1012 HashSet<LayoutNode> visited = new HashSet<>(); 1013 for (int i = 0; i < layers.length; i++) { 1014 for (LayoutNode n : layers[i]) { 1015 assert !visited.contains(n); 1016 assert n.layer == i; 1017 visited.add(n); 1018 } 1019 } 1020 1021 } 1022 } 1023 1024 private class AssignYCoordinates extends AlgorithmPart { 1025 1026 @Override 1027 protected void run() { 1028 int curY = 0; 1029 1030 for (int i = 0; i < layers.length; i++) { 1031 int maxHeight = 0; 1032 int baseLine = 0; 1033 int bottomBaseLine = 0; 1034 for (LayoutNode n : layers[i]) { 1035 maxHeight = Math.max(maxHeight, n.height - n.yOffset - n.bottomYOffset); 1036 baseLine = Math.max(baseLine, n.yOffset); 1037 bottomBaseLine = Math.max(bottomBaseLine, n.bottomYOffset); 1038 } 1039 1040 int maxXOffset = 0; 1041 for (LayoutNode n : layers[i]) { 1042 if (n.vertex == null) { 1043 // Dummy node 1044 n.y = curY; 1045 n.height = maxHeight + baseLine + bottomBaseLine; 1046 1047 } else { 1048 n.y = curY + baseLine + (maxHeight - (n.height - n.yOffset - n.bottomYOffset)) / 2 - n.yOffset; 1049 } 1050 1051 for (LayoutEdge e : n.succs) { 1052 int curXOffset = Math.abs(n.x - e.to.x); 1053 maxXOffset = Math.max(curXOffset, maxXOffset); 1054 } 1055 } 1056 1057 curY += maxHeight + baseLine + bottomBaseLine; 1058 curY += layerOffset + ((int) (Math.sqrt(maxXOffset) * 1.5)); 1059 } 1060 } 1061 } 1062 1063 private class CreateDummyNodes extends AlgorithmPart { 1064 1065 private int oldNodeCount; 1066 1067 @Override 1068 protected void preCheck() { 1069 for (LayoutNode n : nodes) { 1070 for (LayoutEdge e : n.succs) { 1071 assert e.from != null; 1072 assert e.from == n; 1073 assert e.from.layer < e.to.layer; 1074 } 1075 1076 for (LayoutEdge e : n.preds) { 1077 assert e.to != null; 1078 assert e.to == n; 1079 } 1080 } 1081 } 1082 1083 @Override 1084 protected void run() { 1085 oldNodeCount = nodes.size(); 1086 1087 if (combine == Combine.SAME_OUTPUTS) { 1088 1089 Comparator<LayoutEdge> comparator = new Comparator<LayoutEdge>() { 1090 1091 @Override 1092 public int compare(LayoutEdge e1, LayoutEdge e2) { 1093 return e1.to.layer - e2.to.layer; 1094 } 1095 }; 1096 HashMap<Integer, List<LayoutEdge>> portHash = new HashMap<>(); 1097 ArrayList<LayoutNode> currentNodes = new ArrayList<>(nodes); 1098 for (LayoutNode n : currentNodes) { 1099 portHash.clear(); 1100 1101 ArrayList<LayoutEdge> succs = new ArrayList<>(n.succs); 1102 HashMap<Integer, LayoutNode> topNodeHash = new HashMap<>(); 1103 HashMap<Integer, HashMap<Integer, LayoutNode>> bottomNodeHash = new HashMap<>(); 1104 for (LayoutEdge e : succs) { 1105 assert e.from.layer < e.to.layer; 1106 if (e.from.layer != e.to.layer - 1) { 1107 if (maxLayerLength != -1 && e.to.layer - e.from.layer > maxLayerLength/* && e.to.preds.size() > 1 && e.from.succs.size() > 1*/) { 1108 assert maxLayerLength > 2; 1109 e.to.preds.remove(e); 1110 e.from.succs.remove(e); 1111 1112 LayoutEdge topEdge = null; 1113 1114 if (combine == Combine.SAME_OUTPUTS && topNodeHash.containsKey(e.relativeFrom)) { 1115 LayoutNode topNode = topNodeHash.get(e.relativeFrom); 1116 topEdge = new LayoutEdge(); 1117 topEdge.relativeFrom = e.relativeFrom; 1118 topEdge.from = e.from; 1119 topEdge.relativeTo = topNode.width / 2; 1120 topEdge.to = topNode; 1121 topEdge.link = e.link; 1122 topEdge.vip = e.vip; 1123 e.from.succs.add(topEdge); 1124 topNode.preds.add(topEdge); 1125 } else { 1126 1127 LayoutNode topNode = new LayoutNode(); 1128 topNode.layer = e.from.layer + 1; 1129 topNode.width = DUMMY_WIDTH; 1130 topNode.height = DUMMY_HEIGHT; 1131 nodes.add(topNode); 1132 topEdge = new LayoutEdge(); 1133 topEdge.relativeFrom = e.relativeFrom; 1134 topEdge.from = e.from; 1135 topEdge.relativeTo = topNode.width / 2; 1136 topEdge.to = topNode; 1137 topEdge.link = e.link; 1138 topEdge.vip = e.vip; 1139 e.from.succs.add(topEdge); 1140 topNode.preds.add(topEdge); 1141 topNodeHash.put(e.relativeFrom, topNode); 1142 bottomNodeHash.put(e.relativeFrom, new HashMap<Integer, LayoutNode>()); 1143 } 1144 1145 HashMap<Integer, LayoutNode> hash = bottomNodeHash.get(e.relativeFrom); 1146 1147 LayoutNode bottomNode = null; 1148 if (hash.containsKey(e.to.layer)) { 1149 bottomNode = hash.get(e.to.layer); 1150 } else { 1151 1152 bottomNode = new LayoutNode(); 1153 bottomNode.layer = e.to.layer - 1; 1154 bottomNode.width = DUMMY_WIDTH; 1155 bottomNode.height = DUMMY_HEIGHT; 1156 nodes.add(bottomNode); 1157 hash.put(e.to.layer, bottomNode); 1158 } 1159 1160 LayoutEdge bottomEdge = new LayoutEdge(); 1161 bottomEdge.relativeTo = e.relativeTo; 1162 bottomEdge.to = e.to; 1163 bottomEdge.relativeFrom = bottomNode.width / 2; 1164 bottomEdge.from = bottomNode; 1165 bottomEdge.link = e.link; 1166 bottomEdge.vip = e.vip; 1167 e.to.preds.add(bottomEdge); 1168 bottomEdgeHash.put(topEdge, bottomEdge); 1169 bottomNode.succs.add(bottomEdge); 1170 1171 } else { 1172 Integer i = e.relativeFrom; 1173 if (!portHash.containsKey(i)) { 1174 portHash.put(i, new ArrayList<LayoutEdge>()); 1175 } 1176 portHash.get(i).add(e); 1177 } 1178 } 1179 } 1180 1181 succs = new ArrayList<>(n.succs); 1182 for (LayoutEdge e : succs) { 1183 1184 Integer i = e.relativeFrom; 1185 if (portHash.containsKey(i)) { 1186 1187 List<LayoutEdge> list = portHash.get(i); 1188 Collections.sort(list, comparator); 1189 1190 if (list.size() == 1) { 1191 processSingleEdge(list.get(0)); 1192 } else { 1193 1194 int maxLayer = list.get(0).to.layer; 1195 for (LayoutEdge curEdge : list) { 1196 maxLayer = Math.max(maxLayer, curEdge.to.layer); 1197 } 1198 1199 int cnt = maxLayer - n.layer - 1; 1200 LayoutEdge[] edges = new LayoutEdge[cnt]; 1201 LayoutNode[] nodes = new LayoutNode[cnt]; 1202 edges[0] = new LayoutEdge(); 1203 edges[0].from = n; 1204 edges[0].relativeFrom = i; 1205 edges[0].vip = e.vip; 1206 n.succs.add(edges[0]); 1207 1208 nodes[0] = new LayoutNode(); 1209 nodes[0].width = dummyWidth; 1210 nodes[0].height = dummyHeight; 1211 nodes[0].layer = n.layer + 1; 1212 nodes[0].preds.add(edges[0]); 1213 edges[0].to = nodes[0]; 1214 edges[0].relativeTo = nodes[0].width / 2; 1215 for (int j = 1; j < cnt; j++) { 1216 edges[j] = new LayoutEdge(); 1217 edges[j].vip = e.vip; 1218 edges[j].from = nodes[j - 1]; 1219 edges[j].relativeFrom = nodes[j - 1].width / 2; 1220 nodes[j - 1].succs.add(edges[j]); 1221 nodes[j] = new LayoutNode(); 1222 nodes[j].width = dummyWidth; 1223 nodes[j].height = dummyHeight; 1224 nodes[j].layer = n.layer + j + 1; 1225 nodes[j].preds.add(edges[j]); 1226 edges[j].to = nodes[j]; 1227 edges[j].relativeTo = nodes[j].width / 2; 1228 } 1229 1230 for (LayoutEdge curEdge : list) { 1231 assert curEdge.to.layer - n.layer - 2 >= 0; 1232 assert curEdge.to.layer - n.layer - 2 < cnt; 1233 LayoutNode anchor = nodes[curEdge.to.layer - n.layer - 2]; 1234 anchor.succs.add(curEdge); 1235 curEdge.from = anchor; 1236 curEdge.relativeFrom = anchor.width / 2; 1237 n.succs.remove(curEdge); 1238 } 1239 1240 } 1241 1242 portHash.remove(i); 1243 } 1244 } 1245 } 1246 } else if (combine == Combine.SAME_INPUTS) { 1247 throw new UnsupportedOperationException("Currently not supported"); 1248 } else { 1249 ArrayList<LayoutNode> currentNodes = new ArrayList<>(nodes); 1250 for (LayoutNode n : currentNodes) { 1251 for (LayoutEdge e : n.succs) { 1252 processSingleEdge(e); 1253 } 1254 } 1255 } 1256 } 1257 1258 private void processSingleEdge(LayoutEdge e) { 1259 LayoutNode n = e.from; 1260 if (e.to.layer > n.layer + 1) { 1261 LayoutEdge last = e; 1262 for (int i = n.layer + 1; i < last.to.layer; i++) { 1263 last = addBetween(last, i); 1264 } 1265 } 1266 } 1267 1268 private LayoutEdge addBetween(LayoutEdge e, int layer) { 1269 LayoutNode n = new LayoutNode(); 1270 n.width = dummyWidth; 1271 n.height = dummyHeight; 1272 n.layer = layer; 1273 n.preds.add(e); 1274 nodes.add(n); 1275 LayoutEdge result = new LayoutEdge(); 1276 result.vip = e.vip; 1277 n.succs.add(result); 1278 result.from = n; 1279 result.relativeFrom = n.width / 2; 1280 result.to = e.to; 1281 result.relativeTo = e.relativeTo; 1282 e.relativeTo = n.width / 2; 1283 e.to.preds.remove(e); 1284 e.to.preds.add(result); 1285 e.to = n; 1286 return result; 1287 } 1288 1289 @Override 1290 public void printStatistics() { 1291 System.out.println("Dummy nodes created: " + (nodes.size() - oldNodeCount)); 1292 } 1293 1294 @Override 1295 public void postCheck() { 1296 ArrayList<LayoutNode> currentNodes = new ArrayList<>(nodes); 1297 for (LayoutNode n : currentNodes) { 1298 for (LayoutEdge e : n.succs) { 1299 assert e.from.layer == e.to.layer - 1; 1300 } 1301 } 1302 1303 for (int i = 0; i < layers.length; i++) { 1304 assert layers[i].size() > 0; 1305 for (LayoutNode n : layers[i]) { 1306 assert n.layer == i; 1307 } 1308 } 1309 } 1310 } 1311 1312 private class AssignLayers extends AlgorithmPart { 1313 1314 @Override 1315 public void preCheck() { 1316 for (LayoutNode n : nodes) { 1317 assert n.layer == -1; 1318 } 1319 } 1320 1321 @Override 1322 protected void run() { 1323 assignLayerDownwards(); 1324 assignLayerUpwards(); 1325 } 1326 1327 private void assignLayerDownwards() { 1328 ArrayList<LayoutNode> hull = new ArrayList<>(); 1329 for (LayoutNode n : nodes) { 1330 if (n.preds.isEmpty()) { 1331 hull.add(n); 1332 n.layer = 0; 1333 } 1334 } 1335 1336 int z = minLayerDifference; 1337 while (!hull.isEmpty()) { 1338 ArrayList<LayoutNode> newSet = new ArrayList<>(); 1339 for (LayoutNode n : hull) { 1340 for (LayoutEdge se : n.succs) { 1341 LayoutNode s = se.to; 1342 if (s.layer != -1) { 1343 // This node was assigned before. 1344 } else { 1345 boolean unassignedPred = false; 1346 for (LayoutEdge pe : s.preds) { 1347 LayoutNode p = pe.from; 1348 if (p.layer == -1 || p.layer >= z) { 1349 // This now has an unscheduled successor or a successor that was scheduled only in this round. 1350 unassignedPred = true; 1351 break; 1352 } 1353 } 1354 1355 if (unassignedPred) { 1356 // This successor node can not yet be assigned. 1357 } else { 1358 s.layer = z; 1359 newSet.add(s); 1360 } 1361 } 1362 } 1363 } 1364 1365 hull = newSet; 1366 z += minLayerDifference; 1367 } 1368 1369 layerCount = z - minLayerDifference; 1370 for (LayoutNode n : nodes) { 1371 n.layer = (layerCount - 1 - n.layer); 1372 } 1373 } 1374 1375 private void assignLayerUpwards() { 1376 ArrayList<LayoutNode> hull = new ArrayList<>(); 1377 for (LayoutNode n : nodes) { 1378 if (n.succs.isEmpty()) { 1379 hull.add(n); 1380 } else { 1381 n.layer = -1; 1382 } 1383 } 1384 1385 int z = minLayerDifference; 1386 while (!hull.isEmpty()) { 1387 ArrayList<LayoutNode> newSet = new ArrayList<>(); 1388 for (LayoutNode n : hull) { 1389 if (n.layer < z) { 1390 for (LayoutEdge se : n.preds) { 1391 LayoutNode s = se.from; 1392 if (s.layer != -1) { 1393 // This node was assigned before. 1394 } else { 1395 boolean unassignedSucc = false; 1396 for (LayoutEdge pe : s.succs) { 1397 LayoutNode p = pe.to; 1398 if (p.layer == -1 || p.layer >= z) { 1399 // This now has an unscheduled successor or a successor that was scheduled only in this round. 1400 unassignedSucc = true; 1401 break; 1402 } 1403 } 1404 1405 if (unassignedSucc) { 1406 // This predecessor node can not yet be assigned. 1407 } else { 1408 s.layer = z; 1409 newSet.add(s); 1410 } 1411 } 1412 } 1413 } else { 1414 newSet.add(n); 1415 } 1416 } 1417 1418 hull = newSet; 1419 z += minLayerDifference; 1420 } 1421 1422 layerCount = z - minLayerDifference; 1423 1424 for (LayoutNode n : nodes) { 1425 n.layer = (layerCount - 1 - n.layer); 1426 } 1427 } 1428 1429 @Override 1430 public void postCheck() { 1431 for (LayoutNode n : nodes) { 1432 assert n.layer >= 0; 1433 assert n.layer < layerCount; 1434 for (LayoutEdge e : n.succs) { 1435 assert e.from.layer < e.to.layer; 1436 } 1437 } 1438 } 1439 } 1440 1441 private class ReverseEdges extends AlgorithmPart { 1442 1443 private HashSet<LayoutNode> visited; 1444 private HashSet<LayoutNode> active; 1445 1446 @Override 1447 protected void run() { 1448 1449 // Remove self-edges 1450 for (LayoutNode node : nodes) { 1451 ArrayList<LayoutEdge> succs = new ArrayList<>(node.succs); 1452 for (LayoutEdge e : succs) { 1453 assert e.from == node; 1454 if (e.to == node) { 1455 node.succs.remove(e); 1456 node.preds.remove(e); 1457 } 1458 } 1459 } 1460 1461 // Reverse inputs of roots 1462 for (LayoutNode node : nodes) { 1463 if (node.vertex.isRoot()) { 1464 boolean ok = true; 1465 for (LayoutEdge e : node.preds) { 1466 if (e.from.vertex.isRoot()) { 1467 ok = false; 1468 break; 1469 } 1470 } 1471 if (ok) { 1472 reverseAllInputs(node); 1473 } 1474 } 1475 } 1476 1477 // Start DFS and reverse back edges 1478 visited = new HashSet<>(); 1479 active = new HashSet<>(); 1480 for (LayoutNode node : nodes) { 1481 DFS(node); 1482 } 1483 1484 for (LayoutNode node : nodes) { 1485 1486 SortedSet<Integer> reversedDown = new TreeSet<>(); 1487 1488 for (LayoutEdge e : node.succs) { 1489 if (reversedLinks.contains(e.link)) { 1490 reversedDown.add(e.relativeFrom); 1491 } 1492 } 1493 1494 SortedSet<Integer> reversedUp = null; 1495 if (reversedDown.size() == 0) { 1496 reversedUp = new TreeSet<>(Collections.reverseOrder()); 1497 } else { 1498 reversedUp = new TreeSet<>(); 1499 } 1500 1501 for (LayoutEdge e : node.preds) { 1502 if (reversedLinks.contains(e.link)) { 1503 reversedUp.add(e.relativeTo); 1504 } 1505 } 1506 1507 final int offset = X_OFFSET + DUMMY_WIDTH; 1508 1509 int curX = 0; 1510 int curWidth = node.width + reversedDown.size() * offset; 1511 for (int pos : reversedDown) { 1512 ArrayList<LayoutEdge> reversedSuccs = new ArrayList<>(); 1513 for (LayoutEdge e : node.succs) { 1514 if (e.relativeFrom == pos && reversedLinks.contains(e.link)) { 1515 reversedSuccs.add(e); 1516 e.relativeFrom = curWidth; 1517 } 1518 } 1519 1520 ArrayList<Point> startPoints = new ArrayList<>(); 1521 startPoints.add(new Point(curWidth, curX)); 1522 startPoints.add(new Point(pos, curX)); 1523 startPoints.add(new Point(pos, reversedDown.size() * offset)); 1524 for (LayoutEdge e : reversedSuccs) { 1525 reversedLinkStartPoints.put(e.link, startPoints); 1526 } 1527 1528 node.inOffsets.put(pos, -curX); 1529 curX += offset; 1530 node.height += offset; 1531 node.yOffset += offset; 1532 curWidth -= offset; 1533 } 1534 node.width += reversedDown.size() * offset; 1535 1536 if (reversedDown.size() == 0) { 1537 curX = offset; 1538 } else { 1539 curX = -offset; 1540 } 1541 1542 curX = 0; 1543 int minX = 0; 1544 if (reversedDown.size() != 0) { 1545 minX = -offset * reversedUp.size(); 1546 } 1547 1548 int oldNodeHeight = node.height; 1549 for (int pos : reversedUp) { 1550 ArrayList<LayoutEdge> reversedPreds = new ArrayList<>(); 1551 for (LayoutEdge e : node.preds) { 1552 if (e.relativeTo == pos && reversedLinks.contains(e.link)) { 1553 if (reversedDown.size() == 0) { 1554 e.relativeTo = node.width + offset; 1555 } else { 1556 e.relativeTo = curX - offset; 1557 } 1558 1559 reversedPreds.add(e); 1560 } 1561 } 1562 node.height += offset; 1563 ArrayList<Point> endPoints = new ArrayList<>(); 1564 1565 if (reversedDown.size() == 0) { 1566 1567 curX += offset; 1568 node.width += offset; 1569 endPoints.add(new Point(node.width, node.height)); 1570 1571 } else { 1572 curX -= offset; 1573 node.width += offset; 1574 endPoints.add(new Point(curX, node.height)); 1575 } 1576 1577 node.outOffsets.put(pos - minX, curX); 1578 curX += offset; 1579 node.bottomYOffset += offset; 1580 1581 endPoints.add(new Point(pos, node.height)); 1582 endPoints.add(new Point(pos, oldNodeHeight)); 1583 for (LayoutEdge e : reversedPreds) { 1584 reversedLinkEndPoints.put(e.link, endPoints); 1585 } 1586 } 1587 1588 if (minX < 0) { 1589 for (LayoutEdge e : node.preds) { 1590 e.relativeTo -= minX; 1591 } 1592 1593 for (LayoutEdge e : node.succs) { 1594 e.relativeFrom -= minX; 1595 } 1596 1597 node.xOffset = -minX; 1598 node.width += -minX; 1599 } 1600 } 1601 1602 } 1603 1604 private void DFS(LayoutNode startNode) { 1605 if (visited.contains(startNode)) { 1606 return; 1607 } 1608 1609 Stack<LayoutNode> stack = new Stack<>(); 1610 stack.push(startNode); 1611 1612 while (!stack.empty()) { 1613 LayoutNode node = stack.pop(); 1614 1615 if (visited.contains(node)) { 1616 // Node no longer active 1617 active.remove(node); 1618 continue; 1619 } 1620 1621 // Repush immediately to know when no longer active 1622 stack.push(node); 1623 visited.add(node); 1624 active.add(node); 1625 1626 ArrayList<LayoutEdge> succs = new ArrayList<>(node.succs); 1627 for (LayoutEdge e : succs) { 1628 if (active.contains(e.to)) { 1629 assert visited.contains(e.to); 1630 // Encountered back edge 1631 reverseEdge(e); 1632 } else if (!visited.contains(e.to) && (linksToFollow.size() == 0 || linksToFollow.contains(e.link))) { 1633 stack.push(e.to); 1634 } 1635 } 1636 } 1637 } 1638 1639 private void reverseAllInputs(LayoutNode node) { 1640 for (LayoutEdge e : node.preds) { 1641 assert !reversedLinks.contains(e.link); 1642 reversedLinks.add(e.link); 1643 node.succs.add(e); 1644 e.from.preds.add(e); 1645 e.from.succs.remove(e); 1646 int oldRelativeFrom = e.relativeFrom; 1647 int oldRelativeTo = e.relativeTo; 1648 e.to = e.from; 1649 e.from = node; 1650 e.relativeFrom = oldRelativeTo; 1651 e.relativeTo = oldRelativeFrom; 1652 } 1653 node.preds.clear(); 1654 } 1655 1656 private void reverseEdge(LayoutEdge e) { 1657 assert !reversedLinks.contains(e.link); 1658 reversedLinks.add(e.link); 1659 1660 LayoutNode oldFrom = e.from; 1661 LayoutNode oldTo = e.to; 1662 int oldRelativeFrom = e.relativeFrom; 1663 int oldRelativeTo = e.relativeTo; 1664 1665 e.from = oldTo; 1666 e.to = oldFrom; 1667 e.relativeFrom = oldRelativeTo; 1668 e.relativeTo = oldRelativeFrom; 1669 1670 oldFrom.succs.remove(e); 1671 oldFrom.preds.add(e); 1672 oldTo.preds.remove(e); 1673 oldTo.succs.add(e); 1674 } 1675 1676 @Override 1677 public void postCheck() { 1678 1679 for (LayoutNode n : nodes) { 1680 1681 HashSet<LayoutNode> curVisited = new HashSet<>(); 1682 Queue<LayoutNode> queue = new LinkedList<>(); 1683 for (LayoutEdge e : n.succs) { 1684 LayoutNode s = e.to; 1685 queue.add(s); 1686 curVisited.add(s); 1687 } 1688 1689 while (!queue.isEmpty()) { 1690 LayoutNode curNode = queue.remove(); 1691 1692 for (LayoutEdge e : curNode.succs) { 1693 assert e.to != n; 1694 if (!curVisited.contains(e.to)) { 1695 queue.add(e.to); 1696 curVisited.add(e.to); 1697 } 1698 } 1699 } 1700 } 1701 } 1702 } 1703 private Comparator<Link> linkComparator = new Comparator<Link>() { 1704 1705 @Override 1706 public int compare(Link l1, Link l2) { 1707 if (l1.isVIP() && !l2.isVIP()) { 1708 return -1; 1709 } 1710 1711 if (!l1.isVIP() && l2.isVIP()) { 1712 return 1; 1713 } 1714 1715 int result = l1.getFrom().getVertex().compareTo(l2.getFrom().getVertex()); 1716 if (result != 0) { 1717 return result; 1718 } 1719 result = l1.getTo().getVertex().compareTo(l2.getTo().getVertex()); 1720 if (result != 0) { 1721 return result; 1722 } 1723 result = l1.getFrom().getRelativePosition().x - l2.getFrom().getRelativePosition().x; 1724 if (result != 0) { 1725 return result; 1726 } 1727 result = l1.getTo().getRelativePosition().x - l2.getTo().getRelativePosition().x; 1728 return result; 1729 } 1730 }; 1731 1732 private class BuildDatastructure extends AlgorithmPart { 1733 1734 @Override 1735 protected void run() { 1736 // Set up nodes 1737 List<Vertex> vertices = new ArrayList<>(graph.getVertices()); 1738 Collections.sort(vertices); 1739 1740 for (Vertex v : vertices) { 1741 LayoutNode node = new LayoutNode(); 1742 Dimension size = v.getSize(); 1743 node.width = (int) size.getWidth(); 1744 node.height = (int) size.getHeight(); 1745 node.vertex = v; 1746 nodes.add(node); 1747 vertexToLayoutNode.put(v, node); 1748 } 1749 1750 // Set up edges 1751 List<Link> links = new ArrayList<>(graph.getLinks()); 1752 Collections.sort(links, linkComparator); 1753 for (Link l : links) { 1754 LayoutEdge edge = new LayoutEdge(); 1755 assert vertexToLayoutNode.containsKey(l.getFrom().getVertex()); 1756 assert vertexToLayoutNode.containsKey(l.getTo().getVertex()); 1757 edge.from = vertexToLayoutNode.get(l.getFrom().getVertex()); 1758 edge.to = vertexToLayoutNode.get(l.getTo().getVertex()); 1759 edge.relativeFrom = l.getFrom().getRelativePosition().x; 1760 edge.relativeTo = l.getTo().getRelativePosition().x; 1761 edge.link = l; 1762 edge.vip = l.isVIP(); 1763 edge.from.succs.add(edge); 1764 edge.to.preds.add(edge); 1765 //assert edge.from != edge.to; // No self-loops allowed 1766 } 1767 1768 for (Link l : importantLinks) { 1769 if (!vertexToLayoutNode.containsKey(l.getFrom().getVertex()) 1770 || vertexToLayoutNode.containsKey(l.getTo().getVertex())) { 1771 continue; 1772 } 1773 LayoutNode from = vertexToLayoutNode.get(l.getFrom().getVertex()); 1774 LayoutNode to = vertexToLayoutNode.get(l.getTo().getVertex()); 1775 for (LayoutEdge e : from.succs) { 1776 if (e.to == to) { 1777 linksToFollow.add(e.link); 1778 } 1779 } 1780 } 1781 } 1782 1783 @Override 1784 public void postCheck() { 1785 1786 assert vertexToLayoutNode.keySet().size() == nodes.size(); 1787 assert nodes.size() == graph.getVertices().size(); 1788 1789 for (Vertex v : graph.getVertices()) { 1790 1791 LayoutNode node = vertexToLayoutNode.get(v); 1792 assert node != null; 1793 1794 for (LayoutEdge e : node.succs) { 1795 assert e.from == node; 1796 } 1797 1798 for (LayoutEdge e : node.preds) { 1799 assert e.to == node; 1800 } 1801 1802 } 1803 } 1804 } 1805 1806 @Override 1807 public void doRouting(LayoutGraph graph) { 1808 // Do nothing for now 1809 } 1810} 1811