| author | wenzelm | 
| Mon, 05 Jan 2015 22:41:09 +0100 | |
| changeset 59292 | fef652c88263 | 
| parent 59290 | 569a8109eeb2 | 
| child 59302 | 4d985afc0565 | 
| permissions | -rw-r--r-- | 
| 59232 | 1  | 
/* Title: Tools/Graphview/layout.scala  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
2  | 
Author: Markus Kaiser, TU Muenchen  | 
| 59240 | 3  | 
Author: Makarius  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
4  | 
|
| 59261 | 5  | 
DAG layout algorithm, according to:  | 
6  | 
||
7  | 
Georg Sander, "Graph Layout through the VCG Tool", in: Graph Drawing,  | 
|
8  | 
DIMACS International Workshop (GD'94), Springer LNCS 894, 1995.  | 
|
9  | 
||
10  | 
http://dx.doi.org/10.1007/3-540-58950-3_371  | 
|
11  | 
ftp://ftp.cs.uni-sb.de/pub/graphics/vcg/doc/tr-A03-94.ps.gz  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
12  | 
*/  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
13  | 
|
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
14  | 
package isabelle.graphview  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
15  | 
|
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
16  | 
|
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
17  | 
import isabelle._  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
18  | 
|
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
19  | 
|
| 59232 | 20  | 
object Layout  | 
| 49744 | 21  | 
{
 | 
| 59262 | 22  | 
  object Point { val zero: Point = Point(0.0, 0.0) }
 | 
23  | 
case class Point(x: Double, y: Double)  | 
|
| 50469 | 24  | 
|
| 59262 | 25  | 
private type Key = Graph_Display.Node  | 
26  | 
private type Coordinates = Map[Key, Point]  | 
|
27  | 
private type Level = List[Key]  | 
|
28  | 
private type Levels = List[Level]  | 
|
29  | 
||
| 59290 | 30  | 
val empty: Layout =  | 
31  | 
new Layout(Metrics.default, Graph_Display.empty_graph, Map.empty, Map.empty)  | 
|
| 50470 | 32  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
33  | 
val pendulum_iterations = 10  | 
| 
49737
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
34  | 
val minimize_crossings_iterations = 40  | 
| 50469 | 35  | 
|
| 59290 | 36  | 
def make(metrics: Metrics, visible_graph: Graph_Display.Graph): Layout =  | 
| 
49737
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
37  | 
  {
 | 
| 59263 | 38  | 
if (visible_graph.is_empty) empty  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
39  | 
    else {
 | 
| 59263 | 40  | 
val initial_levels = level_map(visible_graph)  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
41  | 
|
| 49744 | 42  | 
val (dummy_graph, dummies, dummy_levels) =  | 
| 59263 | 43  | 
((visible_graph, Map.empty[(Key, Key), List[Key]], initial_levels) /:  | 
44  | 
          visible_graph.edges_iterator) {
 | 
|
45  | 
case ((graph, dummies, levels), (from, to)) =>  | 
|
46  | 
if (levels(to) - levels(from) <= 1) (graph, dummies, levels)  | 
|
47  | 
              else {
 | 
|
48  | 
val (graph1, ds, levels1) = add_dummies(graph, from, to, levels)  | 
|
49  | 
(graph1, dummies + ((from, to) -> ds), levels1)  | 
|
50  | 
}  | 
|
51  | 
}  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
52  | 
|
| 50469 | 53  | 
val levels = minimize_crossings(dummy_graph, level_list(dummy_levels))  | 
54  | 
||
| 
50474
 
6ee044e2d1a7
initial layout coordinates more like old browser;
 
wenzelm 
parents: 
50470 
diff
changeset
 | 
55  | 
val initial_coordinates: Coordinates =  | 
| 
 
6ee044e2d1a7
initial layout coordinates more like old browser;
 
wenzelm 
parents: 
50470 
diff
changeset
 | 
56  | 
        (((Map.empty[Key, Point], 0.0) /: levels) {
 | 
| 
 
6ee044e2d1a7
initial layout coordinates more like old browser;
 
wenzelm 
parents: 
50470 
diff
changeset
 | 
57  | 
case ((coords1, y), level) =>  | 
| 
 
6ee044e2d1a7
initial layout coordinates more like old browser;
 
wenzelm 
parents: 
50470 
diff
changeset
 | 
58  | 
            ((((coords1, 0.0) /: level) {
 | 
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
59  | 
case ((coords2, x), key) =>  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
60  | 
val w2 = metrics.box_width2(key)  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
61  | 
(coords2 + (key -> Point(x + w2, y)), x + 2 * w2 + metrics.box_gap)  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
62  | 
})._1, y + metrics.box_height(level.length))  | 
| 
50474
 
6ee044e2d1a7
initial layout coordinates more like old browser;
 
wenzelm 
parents: 
50470 
diff
changeset
 | 
63  | 
})._1  | 
| 
 
6ee044e2d1a7
initial layout coordinates more like old browser;
 
wenzelm 
parents: 
50470 
diff
changeset
 | 
64  | 
|
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
65  | 
|
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
66  | 
val coords = pendulum(metrics, dummy_graph, levels, initial_coordinates)  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
67  | 
|
| 
49737
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
68  | 
val dummy_coords =  | 
| 50467 | 69  | 
        (Map.empty[(Key, Key), List[Point]] /: dummies.keys) {
 | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
70  | 
case (map, key) => map + (key -> dummies(key).map(coords(_)))  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
71  | 
}  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
72  | 
|
| 59290 | 73  | 
new Layout(metrics, visible_graph, coords, dummy_coords)  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
74  | 
}  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
75  | 
}  | 
| 
49737
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
76  | 
|
| 49744 | 77  | 
|
| 
59245
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
78  | 
def add_dummies(graph: Graph_Display.Graph, from: Key, to: Key, levels: Map[Key, Int])  | 
| 
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
79  | 
: (Graph_Display.Graph, List[Key], Map[Key, Int]) =  | 
| 49744 | 80  | 
  {
 | 
81  | 
val ds =  | 
|
| 
59245
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
82  | 
      ((levels(from) + 1) until levels(to)).map(l => {
 | 
| 
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
83  | 
// FIXME !?  | 
| 
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
84  | 
val ident = "%s$%s$%d".format(from.ident, to.ident, l)  | 
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
85  | 
          Graph_Display.Node(" ", ident)
 | 
| 
59245
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
86  | 
}).toList  | 
| 49744 | 87  | 
|
88  | 
val ls =  | 
|
89  | 
      (levels /: ((levels(from) + 1) until levels(to)).zip(ds)) {
 | 
|
90  | 
case (ls, (l, d)) => ls + (d -> l)  | 
|
91  | 
}  | 
|
92  | 
||
| 
59245
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
93  | 
val graph1 = (graph /: ds)(_.new_node(_, Nil))  | 
| 49744 | 94  | 
val graph2 =  | 
95  | 
      (graph1.del_edge(from, to) /: (from :: ds ::: List(to)).sliding(2)) {
 | 
|
96  | 
case (g, List(x, y)) => g.add_edge(x, y)  | 
|
97  | 
}  | 
|
98  | 
(graph2, ds, ls)  | 
|
99  | 
}  | 
|
100  | 
||
| 
59245
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
101  | 
def level_map(graph: Graph_Display.Graph): Map[Key, Int] =  | 
| 50467 | 102  | 
    (Map.empty[Key, Int] /: graph.topological_order) {
 | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
103  | 
      (levels, key) => {
 | 
| 
49737
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
104  | 
        val lev = 1 + (-1 /: graph.imm_preds(key)) { case (m, key) => m max levels(key) }
 | 
| 
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
105  | 
levels + (key -> lev)  | 
| 
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
106  | 
}  | 
| 
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
107  | 
}  | 
| 50469 | 108  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
109  | 
def level_list(map: Map[Key, Int]): Levels =  | 
| 
49737
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
110  | 
  {
 | 
| 
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
111  | 
    val max_lev = (-1 /: map) { case (m, (_, l)) => m max l }
 | 
| 
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
112  | 
val buckets = new Array[Level](max_lev + 1)  | 
| 
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
113  | 
    for (l <- 0 to max_lev) { buckets(l) = Nil }
 | 
| 
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
114  | 
    for ((key, l) <- map) { buckets(l) = key :: buckets(l) }
 | 
| 
59245
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
115  | 
buckets.iterator.map(_.sorted(Graph_Display.Node.Ordering)).toList  | 
| 
49737
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
116  | 
}  | 
| 
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
117  | 
|
| 
59245
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
118  | 
def count_crossings(graph: Graph_Display.Graph, levels: Levels): Int =  | 
| 49746 | 119  | 
  {
 | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
120  | 
    def in_level(ls: Levels): Int = ls match {
 | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
121  | 
case List(top, bot) =>  | 
| 49746 | 122  | 
        top.iterator.zipWithIndex.map {
 | 
123  | 
case (outer_parent, outer_parent_index) =>  | 
|
124  | 
graph.imm_succs(outer_parent).iterator.map(bot.indexOf(_))  | 
|
125  | 
.map(outer_child =>  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
126  | 
(0 until outer_parent_index)  | 
| 49746 | 127  | 
.map(inner_parent =>  | 
128  | 
graph.imm_succs(top(inner_parent)).iterator.map(bot.indexOf(_))  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
129  | 
.filter(inner_child => outer_child < inner_child)  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
130  | 
.size  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
131  | 
).sum  | 
| 49746 | 132  | 
).sum  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
133  | 
}.sum  | 
| 49746 | 134  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
135  | 
case _ => 0  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
136  | 
}  | 
| 49746 | 137  | 
|
138  | 
levels.iterator.sliding(2).map(ls => in_level(ls.toList)).sum  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
139  | 
}  | 
| 50469 | 140  | 
|
| 
59245
 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 
wenzelm 
parents: 
59240 
diff
changeset
 | 
141  | 
def minimize_crossings(graph: Graph_Display.Graph, levels: Levels): Levels =  | 
| 49745 | 142  | 
  {
 | 
| 50469 | 143  | 
def resort_level(parent: Level, child: Level, top_down: Boolean): Level =  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
144  | 
      child.map(k => {
 | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
145  | 
val ps = if (top_down) graph.imm_preds(k) else graph.imm_succs(k)  | 
| 50469 | 146  | 
val weight =  | 
| 50468 | 147  | 
            (0.0 /: ps) { (w, p) => w + (0 max parent.indexOf(p)) } / (ps.size max 1)
 | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
148  | 
(k, weight)  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
149  | 
}).sortBy(_._2).map(_._1)  | 
| 50469 | 150  | 
|
| 59263 | 151  | 
def resort(levels: Levels, top_down: Boolean) =  | 
152  | 
if (top_down)  | 
|
153  | 
(List(levels.head) /: levels.tail)((tops, bot) =>  | 
|
154  | 
resort_level(tops.head, bot, top_down) :: tops).reverse  | 
|
155  | 
      else {
 | 
|
156  | 
val rev_levels = levels.reverse  | 
|
157  | 
(List(rev_levels.head) /: rev_levels.tail)((bots, top) =>  | 
|
158  | 
resort_level(bots.head, top, top_down) :: bots)  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
159  | 
}  | 
| 50469 | 160  | 
|
| 
49737
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
161  | 
    ((levels, count_crossings(graph, levels), true) /: (1 to minimize_crossings_iterations)) {
 | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
162  | 
      case ((old_levels, old_crossings, top_down), _) => {
 | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
163  | 
val new_levels = resort(old_levels, top_down)  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
164  | 
val new_crossings = count_crossings(graph, new_levels)  | 
| 
49737
 
dd6fc7c9504a
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient;
 
wenzelm 
parents: 
49565 
diff
changeset
 | 
165  | 
if (new_crossings < old_crossings)  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
166  | 
(new_levels, new_crossings, !top_down)  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
167  | 
else  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
168  | 
(old_levels, old_crossings, !top_down)  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
169  | 
}  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
170  | 
}._1  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
171  | 
}  | 
| 50469 | 172  | 
|
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
173  | 
def pendulum(  | 
| 59290 | 174  | 
metrics: Metrics, graph: Graph_Display.Graph,  | 
| 
50474
 
6ee044e2d1a7
initial layout coordinates more like old browser;
 
wenzelm 
parents: 
50470 
diff
changeset
 | 
175  | 
levels: Levels, coords: Map[Key, Point]): Coordinates =  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
176  | 
  {
 | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
177  | 
type Regions = List[List[Region]]  | 
| 50469 | 178  | 
|
| 59263 | 179  | 
def iteration(  | 
180  | 
coords: Coordinates, regions: Regions, top_down: Boolean): (Coordinates, Regions, Boolean) =  | 
|
| 50469 | 181  | 
    {
 | 
| 59263 | 182  | 
val (coords1, regions1, moved) =  | 
183  | 
      ((coords, List.empty[List[Region]], false) /: (if (top_down) regions else regions.reverse)) {
 | 
|
184  | 
case ((coords, tops, moved), bot) =>  | 
|
185  | 
val bot1 = collapse(coords, bot, top_down)  | 
|
186  | 
val (coords1, moved1) = deflect(coords, bot1, top_down)  | 
|
187  | 
(coords1, bot1 :: tops, moved || moved1)  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
188  | 
}  | 
| 59263 | 189  | 
(coords1, regions1.reverse, moved)  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
190  | 
}  | 
| 50469 | 191  | 
|
192  | 
def collapse(coords: Coordinates, level: List[Region], top_down: Boolean): List[Region] =  | 
|
193  | 
    {
 | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
194  | 
if (level.size <= 1) level  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
195  | 
      else {
 | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
196  | 
var next = level  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
197  | 
var regions_changed = true  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
198  | 
        while (regions_changed) {
 | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
199  | 
regions_changed = false  | 
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
200  | 
          for (i <- Range(next.length - 1, 0, -1)) {
 | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
201  | 
val (r1, r2) = (next(i - 1), next(i))  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
202  | 
val d1 = r1.deflection(graph, coords, top_down)  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
203  | 
val d2 = r2.deflection(graph, coords, top_down)  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
204  | 
|
| 50469 | 205  | 
if (// Do regions touch?  | 
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
206  | 
r1.distance(metrics, coords, r2) <= 0.0 &&  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
207  | 
// Do they influence each other?  | 
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
208  | 
(d1 <= 0.0 && d2 < d1 || d2 > 0.0 && d1 > d2 || d1 > 0.0 && d2 < 0.0))  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
209  | 
            {
 | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
210  | 
regions_changed = true  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
211  | 
r1.nodes = r1.nodes ::: r2.nodes  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
212  | 
next = next.filter(next.indexOf(_) != i)  | 
| 50469 | 213  | 
}  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
214  | 
}  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
215  | 
}  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
216  | 
next  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
217  | 
}  | 
| 50469 | 218  | 
}  | 
219  | 
||
| 59263 | 220  | 
def deflect(  | 
221  | 
coords: Coordinates, level: List[Region], top_down: Boolean): (Coordinates, Boolean) =  | 
|
| 50469 | 222  | 
    {
 | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
223  | 
      ((coords, false) /: (0 until level.length)) {
 | 
| 59263 | 224  | 
case ((coords, moved), i) =>  | 
225  | 
val r = level(i)  | 
|
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
226  | 
val d = r.deflection(graph, coords, top_down)  | 
| 59263 | 227  | 
val offset =  | 
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
228  | 
if (d < 0 && i > 0)  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
229  | 
- (level(i - 1).distance(metrics, coords, r) min (- d))  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
230  | 
else if (d >= 0 && i < level.length - 1)  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
231  | 
r.distance(metrics, coords, level(i + 1)) min d  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
232  | 
else d  | 
| 59263 | 233  | 
(r.move(coords, offset), moved || d != 0)  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
234  | 
}  | 
| 50469 | 235  | 
}  | 
236  | 
||
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
237  | 
val regions = levels.map(level => level.map(new Region(_)))  | 
| 50469 | 238  | 
|
| 59263 | 239  | 
    ((coords, regions, true, true) /: (1 to pendulum_iterations)) {
 | 
240  | 
case ((coords, regions, top_down, moved), _) =>  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
241  | 
        if (moved) {
 | 
| 59263 | 242  | 
val (coords1, regions1, m) = iteration(coords, regions, top_down)  | 
243  | 
(coords1, regions1, !top_down, m)  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
244  | 
}  | 
| 59263 | 245  | 
else (coords, regions, !top_down, moved)  | 
246  | 
}._1  | 
|
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
247  | 
}  | 
| 50469 | 248  | 
|
| 59261 | 249  | 
/*This is an auxiliary class which is used by the layout algorithm when  | 
250  | 
calculating coordinates with the "pendulum method". A "region" is a  | 
|
251  | 
group of nodes which "stick together".*/  | 
|
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
252  | 
private class Region(node: Key)  | 
| 50469 | 253  | 
  {
 | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
254  | 
var nodes: List[Key] = List(node)  | 
| 50469 | 255  | 
|
| 59290 | 256  | 
def distance(metrics: Metrics, coords: Coordinates, that: Region): Double =  | 
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
257  | 
    {
 | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
258  | 
val n1 = this.nodes.last; val x1 = coords(n1).x + metrics.box_width2(n1)  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
259  | 
val n2 = that.nodes.head; val x2 = coords(n2).x - metrics.box_width2(n2)  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
260  | 
x2 - x1 - metrics.box_gap  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
261  | 
}  | 
| 50469 | 262  | 
|
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
263  | 
def deflection(graph: Graph_Display.Graph, coords: Coordinates, top_down: Boolean): Double =  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
264  | 
      ((for (a <- nodes.iterator) yield {
 | 
| 59264 | 265  | 
val x = coords(a).x  | 
266  | 
val bs = if (top_down) graph.imm_preds(a) else graph.imm_succs(a)  | 
|
267  | 
bs.iterator.map(coords(_).x - x).sum / (bs.size max 1)  | 
|
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
268  | 
}).sum / nodes.length).round.toDouble  | 
| 50469 | 269  | 
|
| 59262 | 270  | 
def move(coords: Coordinates, dx: Double): Coordinates =  | 
| 
59265
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
271  | 
if (dx == 0.0) coords  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
272  | 
else  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
273  | 
        (coords /: nodes) {
 | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
274  | 
case (cs, node) =>  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
275  | 
val p = cs(node)  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
276  | 
cs + (node -> Point(p.x + dx, p.y))  | 
| 
 
962ad3942ea7
more metrics, with integer coordinates for layout;
 
wenzelm 
parents: 
59264 
diff
changeset
 | 
277  | 
}  | 
| 
49557
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
278  | 
}  | 
| 
 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 
Markus Kaiser <markus.kaiser@in.tum.de> 
parents:  
diff
changeset
 | 
279  | 
}  | 
| 59262 | 280  | 
|
281  | 
final class Layout private(  | 
|
| 59290 | 282  | 
val metrics: Metrics,  | 
283  | 
val visible_graph: Graph_Display.Graph,  | 
|
| 59262 | 284  | 
nodes: Map[Graph_Display.Node, Layout.Point],  | 
285  | 
dummies: Map[Graph_Display.Edge, List[Layout.Point]])  | 
|
286  | 
{
 | 
|
| 59290 | 287  | 
/* nodes */  | 
288  | 
||
| 59262 | 289  | 
def get_node(node: Graph_Display.Node): Layout.Point =  | 
290  | 
nodes.getOrElse(node, Layout.Point.zero)  | 
|
291  | 
||
292  | 
def map_node(node: Graph_Display.Node, f: Layout.Point => Layout.Point): Layout =  | 
|
293  | 
    nodes.get(node) match {
 | 
|
294  | 
case None => this  | 
|
| 59290 | 295  | 
case Some(p) =>  | 
296  | 
val nodes1 = nodes + (node -> f(p))  | 
|
297  | 
new Layout(metrics, visible_graph, nodes1, dummies)  | 
|
| 59262 | 298  | 
}  | 
299  | 
||
300  | 
||
| 59290 | 301  | 
/* dummies */  | 
302  | 
||
| 59262 | 303  | 
def get_dummies(edge: Graph_Display.Edge): List[Layout.Point] =  | 
304  | 
dummies.getOrElse(edge, Nil)  | 
|
305  | 
||
306  | 
def map_dummies(edge: Graph_Display.Edge, f: List[Layout.Point] => List[Layout.Point]): Layout =  | 
|
307  | 
    dummies.get(edge) match {
 | 
|
308  | 
case None => this  | 
|
| 59290 | 309  | 
case Some(ds) =>  | 
310  | 
val dummies1 = dummies + (edge -> f(ds))  | 
|
311  | 
new Layout(metrics, visible_graph, nodes, dummies1)  | 
|
| 59262 | 312  | 
}  | 
| 59292 | 313  | 
|
314  | 
def dummies_iterator: Iterator[Layout.Point] =  | 
|
315  | 
    for { (_, ds) <- dummies.iterator; d <- ds.iterator } yield d
 | 
|
| 59262 | 316  | 
}  | 
317  |