JAL-3253-applet JAL-3383 Overview speed up -- see Issue comments.
authorhansonr <hansonr@STO24954W.ad.stolaf.edu>
Thu, 1 Aug 2019 10:07:19 +0000 (05:07 -0500)
committerhansonr <hansonr@STO24954W.ad.stolaf.edu>
Thu, 1 Aug 2019 10:07:19 +0000 (05:07 -0500)
src/jalview/bin/JalviewJS2.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceI.java
src/jalview/datamodel/features/FeatureStore.java
src/jalview/datamodel/features/SequenceFeatures.java
src/jalview/datamodel/features/SequenceFeaturesI.java
src/jalview/gui/OverviewPanel.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java

index 71c06b3..1ed9f27 100644 (file)
@@ -35,7 +35,7 @@ public class JalviewJS2
     {
       args = new String[] { "open", "examples/uniref50.fa", "features",
           "examples/exampleFeatures.txt"
-          // , "noannotation"
+          , "noannotation"
           , "showoverview"
       };
     }
index 6b52480..7624d2c 100755 (executable)
@@ -1967,15 +1967,15 @@ public class Sequence extends ASequence implements SequenceI
 
     List<SequenceFeature> result = getFeatures().findFeatures(startPos,
             endPos, types);
-    if (datasetSequence != null)
-    {
-      result = datasetSequence.getFeatures().findFeatures(startPos, endPos,
-              types);
-    }
-    else
-    {
-      result = sequenceFeatureStore.findFeatures(startPos, endPos, types);
-    }
+    // if (datasetSequence != null)
+    // {
+    // result = datasetSequence.getFeatures().findFeatures(startPos, endPos,
+    // types);
+    // }
+    // else
+    // {
+    // result = sequenceFeatureStore.findFeatures(startPos, endPos, types);
+    // }
 
     /*
      * if end column is gapped, endPos may be to the right, 
@@ -2152,4 +2152,19 @@ public class Sequence extends ASequence implements SequenceI
   {
     argb = null;
   }
+
+  /**
+   * @author Bob Hanson 2019.07.30
+   * 
+   * allows passing the result ArrayList as a parameter to avoid unnecessary construction
+   * 
+   */
+  @Override
+  public void findFeatures(int column, String type,
+          List<SequenceFeature> result)
+  {
+    getFeatures().findFeatures(findPosition(column - 1), type, result);
+  }
+
+
 }
index e521029..013897d 100755 (executable)
@@ -584,11 +584,37 @@ public interface SequenceI extends ASequenceI
    */
   public int firstResidueOutsideIterator(Iterator<int[]> it);
 
+  /**
+   * @author Bob Hanson 2019.07.30
+   * 
+   * get a 4-byte color, with caching
+   * 
+   */
   public int getColor(int i);
 
-  public int setColor(int i, int rgb);
+  /**
+   * @author Bob Hanson 2019.07.30
+   * 
+   * set a 4-byte color, with caching
+   * 
+   */
+  public int setColor(int i, int argb);
 
+  /**
+   * @author Bob Hanson 2019.07.30
+   * 
+   * allows resetting the color cache
+   * 
+   */
   public void resetColors();
 
+  /**
+   * @author Bob Hanson 2019.07.30
+   * 
+   * allows passing the result ArrayList as a parameter to avoid unnecessary construction
+   * 
+   */
+  void findFeatures(int column, String type, List<SequenceFeature> result);
+
 }
 
index 54c0d59..686ac26 100644 (file)
@@ -90,6 +90,10 @@ public class FeatureStore
 
   float nonPositionalMaxScore;
 
+  private SequenceFeature[] temp = new SequenceFeature[3];
+
+  private boolean isTainted;
+
   /**
    * Constructor
    */
@@ -250,6 +254,7 @@ public class FeatureStore
       features = new IntervalStore<>();
     }
     features.add(feature);
+    isTainted = true;
   }
 
   /**
@@ -602,7 +607,7 @@ public class FeatureStore
     positionalMaxScore = Float.NaN;
     nonPositionalMinScore = Float.NaN;
     nonPositionalMaxScore = Float.NaN;
-
+    isTainted = true;
     /*
      * scan non-positional features for groups and scores
      */
@@ -851,4 +856,107 @@ public class FeatureStore
     }
     return modified;
   }
+
+  /**
+   * Find all features containing this position.
+   * Uses isTainted field to know when to reconstruct its temporary array.
+   * 
+   * @param pos
+   * @return list of SequenceFeatures
+   * @author Bob Hanson 2019.07.30
+   */
+  public void findOverlappingFeatures(int pos, List<SequenceFeature> result)
+  {
+
+    if (contactFeatureStarts != null)
+    {
+      findContacts(contactFeatureStarts, pos, result, true);
+      findContacts(contactFeatureEnds, pos, result, false);
+    }
+    if (features != null)
+    {
+      int n = features.size();
+      if (isTainted)
+      {
+        isTainted = false;
+        if (temp.length < n)
+        {
+          temp = new SequenceFeature[n << 1];
+        }
+        features.toArray(temp);
+      }
+      findOverlaps(temp, n, pos, result);
+    }
+  }
+
+  /**
+   * Binary search for contact start or end at a given (Overview) position.
+   * 
+   * @param l
+   * @param pos
+   * @param result
+   * @param isStart
+   * 
+   * @author Bob Hanson 2019.07.30
+   */
+  private static void findContacts(List<SequenceFeature> l, int pos,
+          List<SequenceFeature> result, boolean isStart)
+  {
+    int low = 0;
+    int high = l.size() - 1;
+    while (low <= high)
+    {
+      int mid = (low + high) >>> 1;
+      SequenceFeature f = l.get(mid);
+      switch (Long.signum((isStart ? f.begin : f.end) - pos))
+      {
+      case -1:
+        low = mid + 1;
+        continue;
+      case 1:
+        high = mid - 1;
+        continue;
+      case 0:
+        int m = mid;
+        result.add(f);
+        // could be "5" in 12345556788 ?
+        while (++mid <= high && (f = l.get(mid)) != null
+                && (isStart ? f.begin : f.end) == pos)
+        {
+          result.add(f);
+        }
+        while (--m >= low && (f = l.get(m)) != null
+                && (isStart ? f.begin : f.end) == pos)
+        {
+          result.add(f);
+        }
+        return;
+      }
+    }
+  }
+
+  /**
+   * Brute force point-interval overlap test
+   * 
+   * @param features
+   * @param n
+   * @param pos
+   * @param result
+   */
+  private static void findOverlaps(SequenceFeature[] features, int n,
+          int pos,
+          List<SequenceFeature> result)
+  {
+    // BH I know, brute force. We need a single-position overlap
+    // method for IntervalStore, I think.
+    for (int i = n; --i >= 0;)
+    {
+      SequenceFeature f = features[i];
+      if (f.begin <= pos && f.end >= pos)
+      {
+        result.add(f);
+      }
+    }
+  }
+
 }
index 9925f2b..93ee71b 100644 (file)
@@ -109,12 +109,10 @@ public class SequenceFeatures implements SequenceFeaturesI
           String... type)
   {
     List<SequenceFeature> result = new ArrayList<>();
-
     for (FeatureStore featureSet : varargToTypes(type))
     {
       result.addAll(featureSet.findOverlappingFeatures(from, to));
     }
-
     return result;
   }
 
@@ -219,6 +217,7 @@ public class SequenceFeatures implements SequenceFeaturesI
       return featureStore.values();
     }
 
+
     List<FeatureStore> types = new ArrayList<>();
     List<String> args = Arrays.asList(type);
     for (Entry<String, FeatureStore> featureType : featureStore.entrySet())
@@ -465,4 +464,21 @@ public class SequenceFeatures implements SequenceFeaturesI
   {
     featureStore.clear();
   }
+
+  /**
+   * Simplified find for features associated with a given position.
+   * 
+   * @author Bob Hanson 2019.07.30
+   */
+  @Override
+  public void findFeatures(int pos, String type, List<SequenceFeature> list)
+  {
+    FeatureStore fs = featureStore.get(type);
+    if (fs != null)
+    {
+      fs.findOverlappingFeatures(pos, list);
+    }
+  }
+
+
 }
index ca0283e..ec4b38c 100644 (file)
@@ -228,4 +228,6 @@ public interface SequenceFeaturesI
    * Deletes all positional and non-positional features
    */
   void deleteAll();
+
+  void findFeatures(int pos, String type, List<SequenceFeature> result);
 }
index cc647a8..532028c 100755 (executable)
@@ -131,7 +131,7 @@ public class OverviewPanel extends JPanel
                 && getHeight() == od.getHeight() + ph)
         {
           // BH: resizing is now exceptionally fast.
-          updateOverviewImage();
+          // updateOverviewImage();
         }
         else
         {
index 13885b4..bed72c5 100644 (file)
@@ -33,6 +33,7 @@ import java.awt.Color;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
+import java.util.ArrayList;
 import java.util.List;
 
 public class FeatureRenderer extends FeatureRendererModel
@@ -417,6 +418,8 @@ public class FeatureRenderer extends FeatureRendererModel
     findAllFeatures();
   }
 
+  private List<SequenceFeature> overlaps = new ArrayList<>();
+
   /**
    * Returns the sequence feature colour rendered at the given column position,
    * or null if none found. The feature of highest render order (i.e. on top) is
@@ -428,12 +431,17 @@ public class FeatureRenderer extends FeatureRendererModel
    * colour for features enclosing a gapped column. Check for gap before calling
    * if different behaviour is wanted.
    * 
+   * BH 2019.07.30 
+   * 
+   * Adds a result ArrayList to parameters in order to avoid an unnecessary construction of that for every pixel checked.
+   * 
+   * 
    * @param seq
    * @param column
    *          (1..)
    * @return
    */
-  Color findFeatureColour(SequenceI seq, int column)
+  private Color findFeatureColour(SequenceI seq, int column)
   {
     /*
      * check for new feature added while processing
@@ -453,16 +461,19 @@ public class FeatureRenderer extends FeatureRendererModel
         continue;
       }
 
-      List<SequenceFeature> overlaps = seq.findFeatures(column, column,
-              type);
-      for (SequenceFeature sequenceFeature : overlaps)
+      overlaps.clear();
+      seq.findFeatures(column, type, overlaps);
+      if (overlaps.size() > 0)
       {
-        if (!featureGroupNotShown(sequenceFeature))
+        for (SequenceFeature sequenceFeature : overlaps)
         {
-          Color col = getColour(sequenceFeature);
-          if (col != null)
+          if (!featureGroupNotShown(sequenceFeature))
           {
-            return col;
+            Color col = getColour(sequenceFeature);
+            if (col != null)
+            {
+              return col;
+            }
           }
         }
       }