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