src/Tools/Graphview/layout.scala
changeset 75393 87ebf5a50283
parent 73359 d8a0e996614b
child 75394 42267c650205
equal deleted inserted replaced
75388:b3ca4a6ed74b 75393:87ebf5a50283
    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     }