src/Tools/Graphview/layout.scala
changeset 59384 c75327a34960
parent 59316 a1238edd8b36
child 59419 2fb2194853cc
equal deleted inserted replaced
59383:1434ef1e0ede 59384:c75327a34960
    53 
    53 
    54   sealed abstract class Vertex
    54   sealed abstract class Vertex
    55   case class Node(node: Graph_Display.Node) extends Vertex
    55   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
    56   case class Dummy(node1: Graph_Display.Node, node2: Graph_Display.Node, index: Int) extends Vertex
    57 
    57 
    58   object Point { val zero: Point = Point(0.0, 0.0) }
    58   sealed case class Info(
    59   case class Point(x: Double, y: Double)
    59     x: Double, y: Double, width2: Double, height2: Double, lines: List[String])
    60 
    60   {
    61   type Graph = isabelle.Graph[Vertex, Point]
    61     def left: Double = x - width2
    62 
    62     def right: Double = x + width2
    63   def make_graph(entries: List[((Vertex, Point), List[Vertex])]): Graph =
    63     def width: Double = 2 * width2
       
    64     def height: Double = 2 * height2
       
    65   }
       
    66 
       
    67   type Graph = isabelle.Graph[Vertex, Info]
       
    68 
       
    69   def make_graph(entries: List[((Vertex, Info), List[Vertex])]): Graph =
    64     isabelle.Graph.make(entries)(Vertex.Ordering)
    70     isabelle.Graph.make(entries)(Vertex.Ordering)
    65 
    71 
    66   val empty_graph: Graph = make_graph(Nil)
    72   val empty_graph: Graph = make_graph(Nil)
    67 
    73 
    68 
    74 
    69   /* vertex x coordinate */
    75   /* vertex x coordinate */
    70 
    76 
    71   private def vertex_left(metrics: Metrics, graph: Graph, v: Vertex): Double =
    77   private def vertex_left(graph: Graph, v: Vertex) = graph.get_node(v).left
    72     graph.get_node(v).x - metrics.box_width2(v)
    78   private def vertex_right(graph: Graph, v: Vertex) = graph.get_node(v).right
    73 
       
    74   private def vertex_right(metrics: Metrics, graph: Graph, v: Vertex): Double =
       
    75     graph.get_node(v).x + metrics.box_width2(v)
       
    76 
    79 
    77   private def move_vertex(graph: Graph, v: Vertex, dx: Double): Graph =
    80   private def move_vertex(graph: Graph, v: Vertex, dx: Double): Graph =
    78     if (dx == 0.0) graph else graph.map_node(v, p => Point(p.x + dx, p.y))
    81     if (dx == 0.0) graph else graph.map_node(v, info => info.copy(x = info.x + dx))
    79 
    82 
    80 
    83 
    81   /* layout */
    84   /* layout */
    82 
    85 
    83   val empty: Layout = new Layout(Metrics.default, Graph_Display.empty_graph, empty_graph)
    86   val empty: Layout = new Layout(Metrics.default, Graph_Display.empty_graph, empty_graph)
    84 
    87 
    85 
    88 
    86   private type Levels = Map[Vertex, Int]
    89   private type Levels = Map[Vertex, Int]
    87   private val empty_levels: Levels = Map.empty
    90   private val empty_levels: Levels = Map.empty
    88 
    91 
    89   def make(options: Options, metrics: Metrics, input_graph: Graph_Display.Graph): Layout =
    92   def make(options: Options, metrics: Metrics,
       
    93     node_text: (Graph_Display.Node, XML.Body) => String,
       
    94     input_graph: Graph_Display.Graph): Layout =
    90   {
    95   {
    91     if (input_graph.is_empty) empty
    96     if (input_graph.is_empty) empty
    92     else {
    97     else {
    93       /* initial graph */
    98       /* initial graph */
    94 
    99 
    95       val initial_graph =
   100       val initial_graph =
    96         make_graph(
   101         make_graph(
    97           input_graph.iterator.map(
   102           input_graph.iterator.map(
    98             { case (a, (_, (_, bs))) => ((Node(a), Point.zero), bs.toList.map(Node(_))) }).toList)
   103             { case (a, (content, (_, bs))) =>
       
   104                 val lines = split_lines(node_text(a, content))
       
   105                 val w2 = metrics.node_width2(lines)
       
   106                 val h2 = metrics.node_height2(lines.length)
       
   107                 ((Node(a), Info(0.0, 0.0, w2, h2, lines)), bs.toList.map(Node(_)))
       
   108             }).toList)
    99 
   109 
   100       val initial_levels: Levels =
   110       val initial_levels: Levels =
   101         (empty_levels /: initial_graph.topological_order) {
   111         (empty_levels /: initial_graph.topological_order) {
   102           case (levels, vertex) =>
   112           case (levels, vertex) =>
   103             val level =
   113             val level =
   105             levels + (vertex -> level)
   115             levels + (vertex -> level)
   106         }
   116         }
   107 
   117 
   108 
   118 
   109       /* graph with dummies */
   119       /* graph with dummies */
       
   120 
       
   121       val dummy_info = Info(0.0, 0.0, metrics.dummy_size2, metrics.dummy_size2, Nil)
   110 
   122 
   111       val (dummy_graph, dummy_levels) =
   123       val (dummy_graph, dummy_levels) =
   112         ((initial_graph, initial_levels) /: input_graph.edges_iterator) {
   124         ((initial_graph, initial_levels) /: input_graph.edges_iterator) {
   113             case ((graph, levels), (node1, node2)) =>
   125             case ((graph, levels), (node1, node2)) =>
   114               val v1 = Node(node1); val l1 = levels(v1)
   126               val v1 = Node(node1); val l1 = levels(v1)
   120                     yield (Dummy(node1, node2, i), l)).toList
   132                     yield (Dummy(node1, node2, i), l)).toList
   121                 val dummies = dummies_levels.map(_._1)
   133                 val dummies = dummies_levels.map(_._1)
   122 
   134 
   123                 val levels1 = (levels /: dummies_levels)(_ + _)
   135                 val levels1 = (levels /: dummies_levels)(_ + _)
   124                 val graph1 =
   136                 val graph1 =
   125                   ((graph /: dummies)(_.new_node(_, Point.zero)).del_edge(v1, v2) /:
   137                   ((graph /: dummies)(_.new_node(_, dummy_info)).del_edge(v1, v2) /:
   126                     (v1 :: dummies ::: List(v2)).sliding(2)) {
   138                     (v1 :: dummies ::: List(v2)).sliding(2)) {
   127                       case (g, List(a, b)) => g.add_edge(a, b) }
   139                       case (g, List(a, b)) => g.add_edge(a, b) }
   128                 (graph1, levels1)
   140                 (graph1, levels1)
   129               }
   141               }
   130           }
   142           }
   135       val levels = minimize_crossings(options, dummy_graph, level_list(dummy_levels))
   147       val levels = minimize_crossings(options, dummy_graph, level_list(dummy_levels))
   136 
   148 
   137       val levels_graph: Graph =
   149       val levels_graph: Graph =
   138         (((dummy_graph, 0.0) /: levels) {
   150         (((dummy_graph, 0.0) /: levels) {
   139           case ((graph, y), level) =>
   151           case ((graph, y), level) =>
       
   152             val num_lines = (0 /: level) { case (n, v) => n max graph.get_node(v).lines.length }
   140             val num_edges = (0 /: level) { case (n, v) => n + graph.imm_succs(v).size }
   153             val num_edges = (0 /: level) { case (n, v) => n + graph.imm_succs(v).size }
   141             ((((graph, 0.0) /: level) {
   154 
   142               case ((g, x), v) =>
   155             val y1 = y + metrics.node_height2(num_lines)
   143                 val w2 = metrics.box_width2(v)
   156 
   144                 (g.map_node(v, _ => Point(x + w2, y)), x + 2 * w2 + metrics.box_gap)
   157             val graph1 =
   145             })._1, y + metrics.box_height(num_edges))
   158               (((graph, 0.0) /: level) { case ((g, x), v) =>
       
   159                 val info = g.get_node(v)
       
   160                 val g1 = g.map_node(v, _ => info.copy(x = x + info.width2, y = y1))
       
   161                 val x1 = x + info.width + metrics.gap
       
   162                 (g1, x1)
       
   163               })._1
       
   164 
       
   165             val y2 = y1 + metrics.level_height2(num_lines, num_edges)
       
   166 
       
   167             (graph1, y2)
   146         })._1
   168         })._1
   147 
   169 
   148 
   170 
   149       /* pendulum/rubberband layout */
   171       /* pendulum/rubberband layout */
   150 
   172 
   222     calculating coordinates with the "pendulum method". A "region" is a
   244     calculating coordinates with the "pendulum method". A "region" is a
   223     group of vertices which "stick together".*/
   245     group of vertices which "stick together".*/
   224   private class Region(val content: List[Vertex])
   246   private class Region(val content: List[Vertex])
   225   {
   247   {
   226     def distance(metrics: Metrics, graph: Graph, that: Region): Double =
   248     def distance(metrics: Metrics, graph: Graph, that: Region): Double =
   227       vertex_left(metrics, graph, that.content.head) -
   249       vertex_left(graph, that.content.head) -
   228       vertex_right(metrics, graph, this.content.last) -
   250       vertex_right(graph, this.content.last) -
   229       metrics.box_gap
   251       metrics.gap
   230 
   252 
   231     def deflection(graph: Graph, top_down: Boolean): Double =
   253     def deflection(graph: Graph, top_down: Boolean): Double =
   232       ((for (a <- content.iterator) yield {
   254       ((for (a <- content.iterator) yield {
   233         val x = graph.get_node(a).x
   255         val x = graph.get_node(a).x
   234         val bs = if (top_down) graph.imm_preds(a) else graph.imm_succs(a)
   256         val bs = if (top_down) graph.imm_preds(a) else graph.imm_succs(a)
   315   }
   337   }
   316 
   338 
   317   private def rubberband(
   339   private def rubberband(
   318     options: Options, metrics: Metrics, levels: List[Level], graph: Graph): Graph =
   340     options: Options, metrics: Metrics, levels: List[Level], graph: Graph): Graph =
   319   {
   341   {
   320     val gap = metrics.box_gap
   342     val gap = metrics.gap
   321     def left(g: Graph, v: Vertex) = vertex_left(metrics, g, v)
       
   322     def right(g: Graph, v: Vertex) = vertex_right(metrics, g, v)
       
   323 
   343 
   324     (graph /: (1 to options.int("graphview_iterations_rubberband"))) { case (graph, _) =>
   344     (graph /: (1 to options.int("graphview_iterations_rubberband"))) { case (graph, _) =>
   325       (graph /: levels) { case (graph, level) =>
   345       (graph /: levels) { case (graph, level) =>
   326         val m = level.length - 1
   346         val m = level.length - 1
   327         (graph /: level.iterator.zipWithIndex) {
   347         (graph /: level.iterator.zipWithIndex) {
   328           case (g, (v, i)) =>
   348           case (g, (v, i)) =>
   329             val d = force_weight(g, v)
   349             val d = force_weight(g, v)
   330             if (d < 0.0 && (i == 0 || right(g, level(i - 1)) + gap < left(g, v) + d) ||
   350             if (d < 0.0 && (i == 0 || vertex_right(g, level(i - 1)) + gap < vertex_left(g, v) + d) ||
   331                 d > 0.0 && (i == m || left(g, level(i + 1)) - gap > right(g, v) + d))
   351                 d > 0.0 && (i == m || vertex_left(g, level(i + 1)) - gap > vertex_right(g, v) + d))
   332               move_vertex(g, v, d.round.toDouble)
   352               move_vertex(g, v, d.round.toDouble)
   333             else g
   353             else g
   334         }
   354         }
   335       }
   355       }
   336     }
   356     }
   342   val input_graph: Graph_Display.Graph,
   362   val input_graph: Graph_Display.Graph,
   343   val output_graph: Layout.Graph)
   363   val output_graph: Layout.Graph)
   344 {
   364 {
   345   /* vertex coordinates */
   365   /* vertex coordinates */
   346 
   366 
   347   def get_vertex(v: Layout.Vertex): Layout.Point =
       
   348     if (output_graph.defined(v)) output_graph.get_node(v)
       
   349     else Layout.Point.zero
       
   350 
       
   351   def translate_vertex(v: Layout.Vertex, dx: Double, dy: Double): Layout =
   367   def translate_vertex(v: Layout.Vertex, dx: Double, dy: Double): Layout =
   352   {
   368   {
   353     if ((dx == 0.0 && dy == 0.0) || !output_graph.defined(v)) this
   369     if ((dx == 0.0 && dy == 0.0) || !output_graph.defined(v)) this
   354     else {
   370     else {
   355       val output_graph1 = output_graph.map_node(v, p => Layout.Point(p.x + dx, p.y + dy))
   371       val output_graph1 =
       
   372         output_graph.map_node(v, info => info.copy(x = info.x + dx, y = info.y + dy))
   356       new Layout(metrics, input_graph, output_graph1)
   373       new Layout(metrics, input_graph, output_graph1)
   357     }
   374     }
   358   }
   375   }
   359 
   376 
   360 
   377 
       
   378   /* regular nodes */
       
   379 
       
   380   def get_node(node: Graph_Display.Node): Layout.Info =
       
   381     output_graph.get_node(Layout.Node(node))
       
   382 
       
   383   def nodes_iterator: Iterator[Layout.Info] =
       
   384     for ((_: Layout.Node, (info, _)) <- output_graph.iterator) yield info
       
   385 
       
   386 
   361   /* dummies */
   387   /* dummies */
   362 
   388 
   363   def dummies_iterator: Iterator[Layout.Point] =
   389   def dummies_iterator: Iterator[Layout.Info] =
   364     for ((_: Layout.Dummy, (p, _)) <- output_graph.iterator) yield p
   390     for ((_: Layout.Dummy, (info, _)) <- output_graph.iterator) yield info
   365 
   391 
   366   def dummies_iterator(edge: Graph_Display.Edge): Iterator[Layout.Point] =
   392   def dummies_iterator(edge: Graph_Display.Edge): Iterator[Layout.Info] =
   367     new Iterator[Layout.Point] {
   393     new Iterator[Layout.Info] {
   368       private var index = 0
   394       private var index = 0
   369       def hasNext: Boolean = output_graph.defined(Layout.Dummy(edge._1, edge._2, index))
   395       def hasNext: Boolean = output_graph.defined(Layout.Dummy(edge._1, edge._2, index))
   370       def next: Layout.Point =
   396       def next: Layout.Info =
   371       {
   397       {
   372         val p = output_graph.get_node(Layout.Dummy(edge._1, edge._2, index))
   398         val info = output_graph.get_node(Layout.Dummy(edge._1, edge._2, index))
   373         index += 1
   399         index += 1
   374         p
   400         info
   375       }
   401       }
   376     }
   402     }
   377 }
   403 }
   378 
   404