JAL-553 JAL-1557 always send selections after triggering the PaintRefreser for a...
[jalview.git] / src / jalview / util / MapList.java
index c32447c..e51442c 100644 (file)
@@ -297,6 +297,8 @@ public class MapList
           int toRatio)
   {
     this();
+    fromRange = coalesceRanges(fromRange);
+    toRange = coalesceRanges(toRange);
     this.fromShifts = fromRange;
     this.toShifts = toRange;
     this.fromRatio = fromRatio;
@@ -320,6 +322,82 @@ public class MapList
   }
 
   /**
+   * Consolidates a list of ranges so that any contiguous ranges are merged.
+   * This assumes the ranges are already in start order (does not sort them).
+   * 
+   * @param ranges
+   * @return the same list (if unchanged), else a new merged list, leaving the
+   *         input list unchanged
+   */
+  public static List<int[]> coalesceRanges(final List<int[]> ranges)
+  {
+    if (ranges == null || ranges.size() < 2) {
+      return ranges;
+    }
+
+    boolean changed = false;
+    List<int[]> merged = new ArrayList<int[]>();
+    int[] lastRange = ranges.get(0);
+    int lastDirection = lastRange[1] >= lastRange[0] ? 1 : -1;
+    lastRange = new int[] { lastRange[0], lastRange[1] };
+    merged.add(lastRange);
+    boolean first = true;
+    
+    for (final int[] range : ranges)
+    {
+      if (first)
+      {
+        first = false;
+        continue;
+      }
+      if (range[0] == lastRange[0] && range[1] == lastRange[1])
+      {
+        // drop duplicate range
+        changed = true;
+        continue;
+      }
+
+      /*
+       * drop this range if it lies within the last range
+       */
+      if ((lastDirection == 1 && range[0] >= lastRange[0]
+              && range[0] <= lastRange[1] && range[1] >= lastRange[0] && range[1] <= lastRange[1])
+              || (lastDirection == -1 && range[0] <= lastRange[0]
+                      && range[0] >= lastRange[1]
+                      && range[1] <= lastRange[0] && range[1] >= lastRange[1]))
+      {
+        changed = true;
+        continue;
+      }
+
+      int direction = range[1] >= range[0] ? 1 : -1;
+
+      /*
+       * if next range is in the same direction as last and contiguous,
+       * just update the end position of the last range
+       */
+      boolean sameDirection = range[1] == range[0] || direction == lastDirection;
+      boolean extending = range[0] == lastRange[1] + lastDirection;
+      boolean overlapping = (lastDirection == 1 && range[0] >= lastRange[0] && range[0] <= lastRange[1])
+              || (lastDirection == -1 && range[0] <= lastRange[0] && range[0] >= lastRange[1]);
+      if (sameDirection && (overlapping || extending))
+      {
+        lastRange[1] = range[1];
+        changed = true;
+      }
+      else
+      {
+        lastRange = new int[] { range[0], range[1] };
+        merged.add(lastRange);
+        // careful: merging [5, 5] after [7, 6] should keep negative direction
+        lastDirection = (range[1] == range[0]) ? lastDirection : direction;
+      }
+    }
+    
+    return changed ? merged : ranges;
+  }
+
+  /**
    * get all mapped positions from 'from' to 'to'
    * 
    * @return int[][] { int[] { fromStart, fromFinish, toStart, toFinish }, int
@@ -894,7 +972,9 @@ public class MapList
     {
       sb.append(" ").append(Arrays.toString(shift));
     }
-    sb.append(" ] To [");
+    sb.append(" ] ");
+    sb.append(fromRatio).append(":").append(toRatio);
+    sb.append(" to [");
     for (int[] shift : toShifts)
     {
       sb.append(" ").append(Arrays.toString(shift));
@@ -927,7 +1007,14 @@ public class MapList
     }
   }
 
-  public static void addRange(int[] range, List<int[]> addTo)
+  /**
+   * Adds the given range to a list of ranges. If the new range just extends
+   * existing ranges, the current endpoint is updated instead.
+   * 
+   * @param range
+   * @param addTo
+   */
+  static void addRange(int[] range, List<int[]> addTo)
   {
     /*
      * list is empty - add to it!