JAL-2526 cache first/last residue column positions in cursor
[jalview.git] / src / jalview / datamodel / Sequence.java
index 27a4163..8ea6ca3 100755 (executable)
@@ -682,11 +682,20 @@ public class Sequence extends ASequence implements SequenceI
 
     int j = start;
     int i = 0;
-    // Rely on end being at least as long as the length of the sequence.
+    int startColumn = 0;
+
+    /*
+     * traverse sequence from the start counting gaps; make a note of
+     * the column of the first residue to save in the cursor
+     */
     while ((i < sequence.length) && (j <= end) && (j <= pos))
     {
       if (!Comparison.isGap(sequence[i]))
       {
+        if (j == start)
+        {
+          startColumn = i;
+        }
         j++;
       }
       i++;
@@ -697,7 +706,7 @@ public class Sequence extends ASequence implements SequenceI
       return end + 1;
     }
 
-    updateCursor(pos, i);
+    updateCursor(pos, i, startColumn);
     return i;
   }
 
@@ -708,10 +717,19 @@ public class Sequence extends ASequence implements SequenceI
    *          (start..)
    * @param column
    *          (1..)
+   * @param startColumn
+   *          column position of the first sequence residue
    */
-  protected void updateCursor(int residuePos, int column)
+  protected void updateCursor(int residuePos, int column, int startColumn)
   {
-    cursor = new SequenceCursor(this, residuePos, column, this.changeCount);
+    int endColumn = cursor == null ? 0 : cursor.lastColumnPosition;
+    if (residuePos == this.end)
+    {
+      endColumn = column;
+    }
+
+    cursor = new SequenceCursor(this, residuePos, column, startColumn,
+            endColumn, this.changeCount);
   }
 
   /**
@@ -760,7 +778,7 @@ public class Sequence extends ASequence implements SequenceI
     }
 
     col++; // convert back to base 1
-    updateCursor(pos, col);
+    updateCursor(pos, col, curs.firstColumnPosition);
 
     return col;
   }
@@ -778,13 +796,19 @@ public class Sequence extends ASequence implements SequenceI
     {
       return findPosition(column + 1, cursor);
     }
-
+    
     // TODO recode this more naturally i.e. count residues only
     // as they are found, not 'in anticipation'
 
+    /*
+     * traverse the sequence counting gaps; note the column position
+     * of the first residue, to save in the cursor
+     */
+    int firstResidueColumn = 0;
     int lastPosFound = 0;
     int lastPosFoundColumn = 0;
     int seqlen = sequence.length;
+
     if (seqlen > 0 && !Comparison.isGap(sequence[0]))
     {
       lastPosFound = start;
@@ -800,6 +824,10 @@ public class Sequence extends ASequence implements SequenceI
       {
         lastPosFound = pos;
         lastPosFoundColumn = j;
+        if (pos == this.start)
+        {
+          firstResidueColumn = j;
+        }
         pos++;
       }
       j++;
@@ -808,6 +836,10 @@ public class Sequence extends ASequence implements SequenceI
     {
       lastPosFound = pos;
       lastPosFoundColumn = j;
+      if (pos == this.start)
+      {
+        firstResidueColumn = j;
+      }
     }
 
     /*
@@ -816,7 +848,8 @@ public class Sequence extends ASequence implements SequenceI
      */
     if (lastPosFound != 0)
     {
-      updateCursor(lastPosFound, lastPosFoundColumn + 1);
+      updateCursor(lastPosFound, lastPosFoundColumn + 1,
+              firstResidueColumn + 1);
     }
 
     return pos;
@@ -876,9 +909,31 @@ public class Sequence extends ASequence implements SequenceI
       return curs.residuePosition; // easy case :-)
     }
 
+    if (curs.lastColumnPosition > 0 && curs.lastColumnPosition <= col)
+    {
+      /*
+       * sequence lies entirely to the left of col
+       * - return last residue + 1
+       */
+      return end + 1;
+    }
+
+    if (curs.firstColumnPosition > 0 && curs.firstColumnPosition >= col)
+    {
+      /*
+       * sequence lies entirely to the right of col
+       * - return first residue
+       */
+      return start;
+    }
+
+    // todo could choose closest to col out of column,
+    // firstColumnPosition, lastColumnPosition as a start point
+
     /*
      * move left or right to find pos from cursor position
      */
+    int firstResidueColumn = curs.firstColumnPosition;
     int column = curs.columnPosition - 1; // to base 0
     int newPos = curs.residuePosition;
     int delta = curs.columnPosition > col ? -1 : 1;
@@ -899,12 +954,17 @@ public class Sequence extends ASequence implements SequenceI
         newPos += delta;
         lastFoundPosition = newPos;
         lastFoundPositionColumn = column + 1;
+        if (lastFoundPosition == this.start)
+        {
+          firstResidueColumn = column + 1;
+        }
       }
     }
 
     if (cursor == null || lastFoundPosition != cursor.residuePosition)
     {
-      updateCursor(lastFoundPosition, lastFoundPositionColumn);
+      updateCursor(lastFoundPosition, lastFoundPositionColumn,
+              firstResidueColumn);
     }
 
     /*
@@ -1704,7 +1764,12 @@ public class Sequence extends ASequence implements SequenceI
   {
     int startPos = findPosition(fromColumn - 1); // convert base 1 to base 0
     int endPos = findPosition(toColumn - 1);
-
+    // to trace / debug behaviour:
+    // System.out
+    // .println(String
+    // .format("%s.findFeatures columns [%d-%d] positions [%d-%d] leaves cursor %s",
+    // getName(), fromColumn, toColumn, startPos,
+    // endPos, cursor));
     List<SequenceFeature> result = new ArrayList<>();
     if (datasetSequence != null)
     {