equal
deleted
inserted
replaced
15 |
15 |
16 |
16 |
17 import isabelle._ |
17 import isabelle._ |
18 |
18 |
19 |
19 |
20 object Layout |
20 object Layout { |
21 { |
|
22 /* graph structure */ |
21 /* graph structure */ |
23 |
22 |
24 object Vertex |
23 object Vertex { |
25 { |
24 object Ordering extends scala.math.Ordering[Vertex] { |
26 object Ordering extends scala.math.Ordering[Vertex] |
|
27 { |
|
28 def compare(v1: Vertex, v2: Vertex): Int = |
25 def compare(v1: Vertex, v2: Vertex): Int = |
29 (v1, v2) match { |
26 (v1, v2) match { |
30 case (Node(a), Node(b)) => Graph_Display.Node.Ordering.compare(a, b) |
27 case (Node(a), Node(b)) => Graph_Display.Node.Ordering.compare(a, b) |
31 case (Dummy(a1, a2, i), Dummy(b1, b2, j)) => |
28 case (Dummy(a1, a2, i), Dummy(b1, b2, j)) => |
32 Graph_Display.Node.Ordering.compare(a1, b1) match { |
29 Graph_Display.Node.Ordering.compare(a1, b1) match { |
54 sealed abstract class Vertex |
51 sealed abstract class Vertex |
55 case class Node(node: Graph_Display.Node) extends Vertex |
52 case class Node(node: Graph_Display.Node) extends Vertex |
56 case class Dummy(node1: Graph_Display.Node, node2: Graph_Display.Node, index: Int) extends Vertex |
53 case class Dummy(node1: Graph_Display.Node, node2: Graph_Display.Node, index: Int) extends Vertex |
57 |
54 |
58 sealed case class Info( |
55 sealed case class Info( |
59 x: Double, y: Double, width2: Double, height2: Double, lines: List[String]) |
56 x: Double, |
60 { |
57 y: Double, |
|
58 width2: Double, |
|
59 height2: Double, |
|
60 lines: List[String] |
|
61 ) { |
61 def left: Double = x - width2 |
62 def left: Double = x - width2 |
62 def right: Double = x + width2 |
63 def right: Double = x + width2 |
63 def width: Double = 2 * width2 |
64 def width: Double = 2 * width2 |
64 def height: Double = 2 * height2 |
65 def height: Double = 2 * height2 |
65 } |
66 } |
87 |
88 |
88 |
89 |
89 private type Levels = Map[Vertex, Int] |
90 private type Levels = Map[Vertex, Int] |
90 private val empty_levels: Levels = Map.empty |
91 private val empty_levels: Levels = Map.empty |
91 |
92 |
92 def make(options: Options, metrics: Metrics, |
93 def make( |
|
94 options: Options, |
|
95 metrics: Metrics, |
93 node_text: (Graph_Display.Node, XML.Body) => String, |
96 node_text: (Graph_Display.Node, XML.Body) => String, |
94 input_graph: Graph_Display.Graph): Layout = |
97 input_graph: Graph_Display.Graph |
95 { |
98 ): Layout = { |
96 if (input_graph.is_empty) empty |
99 if (input_graph.is_empty) empty |
97 else { |
100 else { |
98 /* initial graph */ |
101 /* initial graph */ |
99 |
102 |
100 val initial_graph = |
103 val initial_graph = |
185 /** edge crossings **/ |
188 /** edge crossings **/ |
186 |
189 |
187 private type Level = List[Vertex] |
190 private type Level = List[Vertex] |
188 |
191 |
189 private def minimize_crossings( |
192 private def minimize_crossings( |
190 options: Options, graph: Graph, levels: List[Level]): List[Level] = |
193 options: Options, |
191 { |
194 graph: Graph, |
|
195 levels: List[Level] |
|
196 ): List[Level] = { |
192 def resort(parent: Level, child: Level, top_down: Boolean): Level = |
197 def resort(parent: Level, child: Level, top_down: Boolean): Level = |
193 child.map(v => |
198 child.map(v => { |
194 { |
|
195 val ps = if (top_down) graph.imm_preds(v) else graph.imm_succs(v) |
199 val ps = if (top_down) graph.imm_preds(v) else graph.imm_succs(v) |
196 val weight = |
200 val weight = |
197 ps.foldLeft(0.0) { case (w, p) => w + (0 max parent.indexOf(p)) } / (ps.size max 1) |
201 ps.foldLeft(0.0) { case (w, p) => w + (0 max parent.indexOf(p)) } / (ps.size max 1) |
198 (v, weight) |
202 (v, weight) |
199 }).sortBy(_._2).map(_._1) |
203 }).sortBy(_._2).map(_._1) |
220 else |
224 else |
221 (old_levels, old_crossings) |
225 (old_levels, old_crossings) |
222 }._1 |
226 }._1 |
223 } |
227 } |
224 |
228 |
225 private def level_list(levels: Levels): List[Level] = |
229 private def level_list(levels: Levels): List[Level] = { |
226 { |
|
227 val max_lev = levels.foldLeft(-1) { case (m, (_, l)) => m max l } |
230 val max_lev = levels.foldLeft(-1) { case (m, (_, l)) => m max l } |
228 val buckets = new Array[Level](max_lev + 1) |
231 val buckets = new Array[Level](max_lev + 1) |
229 for (l <- 0 to max_lev) { buckets(l) = Nil } |
232 for (l <- 0 to max_lev) { buckets(l) = Nil } |
230 for ((v, l) <- levels) { buckets(l) = v :: buckets(l) } |
233 for ((v, l) <- levels) { buckets(l) = v :: buckets(l) } |
231 buckets.iterator.map(_.sorted(Vertex.Ordering)).toList |
234 buckets.iterator.map(_.sorted(Vertex.Ordering)).toList |
249 /** pendulum method **/ |
252 /** pendulum method **/ |
250 |
253 |
251 /*This is an auxiliary class which is used by the layout algorithm when |
254 /*This is an auxiliary class which is used by the layout algorithm when |
252 calculating coordinates with the "pendulum method". A "region" is a |
255 calculating coordinates with the "pendulum method". A "region" is a |
253 group of vertices which "stick together".*/ |
256 group of vertices which "stick together".*/ |
254 private class Region(val content: List[Vertex]) |
257 private class Region(val content: List[Vertex]) { |
255 { |
|
256 def distance(metrics: Metrics, graph: Graph, that: Region): Double = |
258 def distance(metrics: Metrics, graph: Graph, that: Region): Double = |
257 vertex_left(graph, that.content.head) - |
259 vertex_left(graph, that.content.head) - |
258 vertex_right(graph, this.content.last) - |
260 vertex_right(graph, this.content.last) - |
259 metrics.gap |
261 metrics.gap |
260 |
262 |
270 |
272 |
271 def combine(that: Region): Region = new Region(content ::: that.content) |
273 def combine(that: Region): Region = new Region(content ::: that.content) |
272 } |
274 } |
273 |
275 |
274 private def pendulum( |
276 private def pendulum( |
275 options: Options, metrics: Metrics, levels: List[Level], levels_graph: Graph): Graph = |
277 options: Options, |
276 { |
278 metrics: Metrics, |
|
279 levels: List[Level], |
|
280 levels_graph: Graph |
|
281 ): Graph = { |
277 def combine_regions(graph: Graph, top_down: Boolean, level: List[Region]): List[Region] = |
282 def combine_regions(graph: Graph, top_down: Boolean, level: List[Region]): List[Region] = |
278 level match { |
283 level match { |
279 case r1 :: rest => |
284 case r1 :: rest => |
280 val rest1 = combine_regions(graph, top_down, rest) |
285 val rest1 = combine_regions(graph, top_down, rest) |
281 rest1 match { |
286 rest1 match { |
291 case _ => r1 :: rest1 |
296 case _ => r1 :: rest1 |
292 } |
297 } |
293 case _ => level |
298 case _ => level |
294 } |
299 } |
295 |
300 |
296 def deflect(level: List[Region], top_down: Boolean, graph: Graph): (Graph, Boolean) = |
301 def deflect(level: List[Region], top_down: Boolean, graph: Graph): (Graph, Boolean) = { |
297 { |
|
298 level.indices.foldLeft((graph, false)) { |
302 level.indices.foldLeft((graph, false)) { |
299 case ((graph, moved), i) => |
303 case ((graph, moved), i) => |
300 val r = level(i) |
304 val r = level(i) |
301 val d = r.deflection(graph, top_down) |
305 val d = r.deflection(graph, top_down) |
302 val dx = |
306 val dx = |
334 |
338 |
335 |
339 |
336 |
340 |
337 /** rubberband method **/ |
341 /** rubberband method **/ |
338 |
342 |
339 private def force_weight(graph: Graph, v: Vertex): Double = |
343 private def force_weight(graph: Graph, v: Vertex): Double = { |
340 { |
|
341 val preds = graph.imm_preds(v) |
344 val preds = graph.imm_preds(v) |
342 val succs = graph.imm_succs(v) |
345 val succs = graph.imm_succs(v) |
343 val n = preds.size + succs.size |
346 val n = preds.size + succs.size |
344 if (n == 0) 0.0 |
347 if (n == 0) 0.0 |
345 else { |
348 else { |
347 ((preds.iterator ++ succs.iterator).map(w => graph.get_node(w).x - x)).sum / n |
350 ((preds.iterator ++ succs.iterator).map(w => graph.get_node(w).x - x)).sum / n |
348 } |
351 } |
349 } |
352 } |
350 |
353 |
351 private def rubberband( |
354 private def rubberband( |
352 options: Options, metrics: Metrics, levels: List[Level], graph: Graph): Graph = |
355 options: Options, |
353 { |
356 metrics: Metrics, |
|
357 levels: List[Level], |
|
358 graph: Graph |
|
359 ): Graph = { |
354 val gap = metrics.gap |
360 val gap = metrics.gap |
355 |
361 |
356 (1 to (2 * options.int("graphview_iterations_rubberband"))).foldLeft(graph) { |
362 (1 to (2 * options.int("graphview_iterations_rubberband"))).foldLeft(graph) { |
357 case (graph, _) => |
363 case (graph, _) => |
358 levels.foldLeft(graph) { case (graph, level) => |
364 levels.foldLeft(graph) { case (graph, level) => |
371 } |
377 } |
372 |
378 |
373 final class Layout private( |
379 final class Layout private( |
374 val metrics: Metrics, |
380 val metrics: Metrics, |
375 val input_graph: Graph_Display.Graph, |
381 val input_graph: Graph_Display.Graph, |
376 val output_graph: Layout.Graph) |
382 val output_graph: Layout.Graph |
377 { |
383 ) { |
378 /* vertex coordinates */ |
384 /* vertex coordinates */ |
379 |
385 |
380 def translate_vertex(v: Layout.Vertex, dx: Double, dy: Double): Layout = |
386 def translate_vertex(v: Layout.Vertex, dx: Double, dy: Double): Layout = { |
381 { |
|
382 if ((dx == 0.0 && dy == 0.0) || !output_graph.defined(v)) this |
387 if ((dx == 0.0 && dy == 0.0) || !output_graph.defined(v)) this |
383 else { |
388 else { |
384 val output_graph1 = |
389 val output_graph1 = |
385 output_graph.map_node(v, info => info.copy(x = info.x + dx, y = info.y + dy)) |
390 output_graph.map_node(v, info => info.copy(x = info.x + dx, y = info.y + dy)) |
386 new Layout(metrics, input_graph, output_graph1) |
391 new Layout(metrics, input_graph, output_graph1) |
404 |
409 |
405 def dummies_iterator(edge: Graph_Display.Edge): Iterator[Layout.Info] = |
410 def dummies_iterator(edge: Graph_Display.Edge): Iterator[Layout.Info] = |
406 new Iterator[Layout.Info] { |
411 new Iterator[Layout.Info] { |
407 private var index = 0 |
412 private var index = 0 |
408 def hasNext: Boolean = output_graph.defined(Layout.Dummy(edge._1, edge._2, index)) |
413 def hasNext: Boolean = output_graph.defined(Layout.Dummy(edge._1, edge._2, index)) |
409 def next(): Layout.Info = |
414 def next(): Layout.Info = { |
410 { |
|
411 val info = output_graph.get_node(Layout.Dummy(edge._1, edge._2, index)) |
415 val info = output_graph.get_node(Layout.Dummy(edge._1, edge._2, index)) |
412 index += 1 |
416 index += 1 |
413 info |
417 info |
414 } |
418 } |
415 } |
419 } |