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 |