src/Tools/Graphview/layout.scala
changeset 59315 2f4d64fba0d7
parent 59313 d7f4f46e9a59
child 59316 a1238edd8b36
--- a/src/Tools/Graphview/layout.scala	Wed Jan 07 09:06:20 2015 +0100
+++ b/src/Tools/Graphview/layout.scala	Wed Jan 07 14:06:52 2015 +0100
@@ -221,10 +221,8 @@
   /*This is an auxiliary class which is used by the layout algorithm when
     calculating coordinates with the "pendulum method". A "region" is a
     group of vertices which "stick together".*/
-  private class Region(init: Vertex)
+  private class Region(val content: List[Vertex])
   {
-    var content: List[Vertex] = List(init)
-
     def distance(metrics: Metrics, graph: Graph, that: Region): Double =
       vertex_left(metrics, graph, that.content.head) -
       vertex_right(metrics, graph, this.content.last) -
@@ -239,54 +237,33 @@
 
     def move(graph: Graph, dx: Double): Graph =
       if (dx == 0.0) graph else (graph /: content)(move_vertex(_, _, dx))
+
+    def combine(that: Region): Region = new Region(content ::: that.content)
   }
 
-  private type Regions = List[List[Region]]
-
   private def pendulum(
     options: Options, metrics: Metrics, levels: List[Level], levels_graph: Graph): Graph =
   {
-    def iteration(graph: Graph, regions: Regions, top_down: Boolean): (Graph, Regions, Boolean) =
-    {
-      val (graph1, regions1, moved) =
-      ((graph, List.empty[List[Region]], false) /: (if (top_down) regions else regions.reverse)) {
-        case ((graph, tops, moved), bot) =>
-          val bot1 = collapse(graph, bot, top_down)
-          val (graph1, moved1) = deflect(graph, bot1, top_down)
-          (graph1, bot1 :: tops, moved || moved1)
+    def combine_regions(graph: Graph, top_down: Boolean, level: List[Region]): List[Region] =
+      level match {
+        case r1 :: rest =>
+          val rest1 = combine_regions(graph, top_down, rest)
+          rest1 match {
+            case r2 :: rest2 =>
+              val d1 = r1.deflection(graph, top_down)
+              val d2 = r2.deflection(graph, top_down)
+              if (// Do regions touch?
+                  r1.distance(metrics, graph, r2) <= 0.0 &&
+                  // Do they influence each other?
+                  (d1 <= 0.0 && d2 < d1 || d2 > 0.0 && d1 > d2 || d1 > 0.0 && d2 < 0.0))
+                r1.combine(r2) :: rest2
+              else r1 :: rest1
+            case _ => r1 :: rest1
+          }
+        case _ => level
       }
-      (graph1, regions1.reverse, moved)
-    }
 
-    def collapse(graph: Graph, level: List[Region], top_down: Boolean): List[Region] =
-    {
-      if (level.size <= 1) level
-      else {
-        var next = level
-        var regions_changed = true
-        while (regions_changed) {
-          regions_changed = false
-          for (i <- Range(next.length - 1, 0, -1)) {
-            val (r1, r2) = (next(i - 1), next(i))
-            val d1 = r1.deflection(graph, top_down)
-            val d2 = r2.deflection(graph, top_down)
-
-            if (// Do regions touch?
-                r1.distance(metrics, graph, r2) <= 0.0 &&
-                // Do they influence each other?
-                (d1 <= 0.0 && d2 < d1 || d2 > 0.0 && d1 > d2 || d1 > 0.0 && d2 < 0.0))
-            {
-              regions_changed = true
-              r1.content = r1.content ::: r2.content
-              next = next.filter(next.indexOf(_) != i)
-            }
-          }
-        }
-        next
-      }
-    }
-
-    def deflect(graph: Graph, level: List[Region], top_down: Boolean): (Graph, Boolean) =
+    def deflect(level: List[Region], top_down: Boolean, graph: Graph): (Graph, Boolean) =
     {
       ((graph, false) /: (0 until level.length)) {
         case ((graph, moved), i) =>
@@ -302,14 +279,20 @@
       }
     }
 
-    val initial_regions = levels.map(level => level.map(new Region(_)))
+    val initial_regions = levels.map(level => level.map(l => new Region(List(l))))
 
     ((levels_graph, initial_regions, true, true) /:
       (1 to options.int("graphview_iterations_pendulum"))) {
       case ((graph, regions, top_down, moved), _) =>
         if (moved) {
-          val (graph1, regions1, moved1) = iteration(graph, regions, top_down)
-          (graph1, regions1, !top_down, moved1)
+          val (graph1, regions1, moved1) =
+            ((graph, List.empty[List[Region]], false) /:
+              (if (top_down) regions else regions.reverse)) { case ((graph, tops, moved), bot) =>
+                val bot1 = combine_regions(graph, top_down, bot)
+                val (graph1, moved1) = deflect(bot1, top_down, graph)
+                (graph1, bot1 :: tops, moved || moved1)
+            }
+          (graph1, regions1.reverse, !top_down, moved1)
         }
         else (graph, regions, !top_down, moved)
     }._1