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