Merge branch 'develop' into features/JAL-2446NCList
[jalview.git] / src / jalview / renderer / seqfeatures / FeatureRenderer.java
index d6be4c2..e81e519 100644 (file)
@@ -21,6 +21,8 @@
 package jalview.renderer.seqfeatures;
 
 import jalview.api.AlignViewportI;
+import jalview.api.FeatureColourI;
+import jalview.datamodel.Range;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.util.Comparison;
@@ -99,8 +101,7 @@ public class FeatureRenderer extends FeatureRendererModel
 
       g.setColor(featureColour);
 
-      g.fillRect((i - start) * charWidth, y1, charWidth,
-              charHeight);
+      g.fillRect((i - start) * charWidth, y1, charWidth, charHeight);
 
       if (colourOnly || !validCharWidth)
       {
@@ -109,8 +110,8 @@ public class FeatureRenderer extends FeatureRendererModel
 
       g.setColor(Color.white);
       int charOffset = (charWidth - fm.charWidth(s)) / 2;
-      g.drawString(String.valueOf(s), charOffset
-              + (charWidth * (i - start)), pady);
+      g.drawString(String.valueOf(s),
+              charOffset + (charWidth * (i - start)), pady);
     }
     return true;
   }
@@ -198,8 +199,8 @@ public class FeatureRenderer extends FeatureRendererModel
 
       g.setColor(Color.black);
       int charOffset = (charWidth - fm.charWidth(s)) / 2;
-      g.drawString(String.valueOf(s), charOffset
-              + (charWidth * (i - start)), pady);
+      g.drawString(String.valueOf(s),
+              charOffset + (charWidth * (i - start)), pady);
     }
     return true;
   }
@@ -217,7 +218,11 @@ public class FeatureRenderer extends FeatureRendererModel
 
     if (Comparison.isGap(seq.getCharAt(column)))
     {
-      return Color.white;
+      /*
+       * returning null allows the colour scheme to provide gap colour
+       * - normally white, but can be customised
+       */
+      return null;
     }
 
     Color renderedColour = null;
@@ -226,7 +231,7 @@ public class FeatureRenderer extends FeatureRendererModel
       /*
        * simple case - just find the topmost rendered visible feature colour
        */
-      renderedColour = findFeatureColour(seq, seq.findPosition(column));
+      renderedColour = findFeatureColour(seq, column);
     }
     else
     {
@@ -263,7 +268,11 @@ public class FeatureRenderer extends FeatureRendererModel
           final SequenceI seq, int start, int end, int y1,
           boolean colourOnly)
   {
-    if (!seq.getFeatures().hasFeatures())
+    /*
+     * if columns are all gapped, or sequence has no features, nothing to do
+     */
+    Range visiblePositions = seq.findPositions(start+1, end+1);
+    if (visiblePositions == null || !seq.getFeatures().hasFeatures())
     {
       return null;
     }
@@ -277,9 +286,6 @@ public class FeatureRenderer extends FeatureRendererModel
               transparency));
     }
 
-    int startPos = seq.findPosition(start);
-    int endPos = seq.findPosition(end);// todo a performant overload of this!
-
     Color drawnColour = null;
 
     /*
@@ -293,38 +299,61 @@ public class FeatureRenderer extends FeatureRendererModel
         continue;
       }
 
-      List<SequenceFeature> overlaps = seq.findFeatures(startPos, endPos,
-              type);
-      for (SequenceFeature sequenceFeature : overlaps)
+      FeatureColourI fc = getFeatureStyle(type);
+      List<SequenceFeature> overlaps = seq.getFeatures().findFeatures(
+              visiblePositions.getBegin(), visiblePositions.getEnd(), type);
+
+      filterFeaturesForDisplay(overlaps, fc);
+
+      for (SequenceFeature sf : overlaps)
       {
+        Color featureColour = fc.getColor(sf);
+        if (featureColour == null)
+        {
+          // score feature outwith threshold for colouring
+          continue;
+        }
+
         /*
-         * a feature type may be flagged as shown but the group 
-         * an instance of it belongs to may be hidden
+         * if feature starts/ends outside the visible range,
+         * restrict to visible positions (or if a contact feature,
+         * to a single position)
          */
-        if (featureGroupNotShown(sequenceFeature))
+        int visibleStart = sf.getBegin();
+        if (visibleStart < visiblePositions.getBegin())
         {
-          continue;
+          visibleStart = sf.isContactFeature() ? sf.getEnd()
+                  : visiblePositions.getBegin();
+        }
+        int visibleEnd = sf.getEnd();
+        if (visibleEnd > visiblePositions.getEnd())
+        {
+          visibleEnd = sf.isContactFeature() ? sf.getBegin()
+                  : visiblePositions.getEnd();
         }
 
-        Color featureColour = getColour(sequenceFeature);
-        boolean isContactFeature = sequenceFeature.isContactFeature();
+        int featureStartCol = seq.findIndex(visibleStart);
+        int featureEndCol = sf.begin == sf.end ? featureStartCol : seq
+                .findIndex(visibleEnd);
+
+        // Color featureColour = getColour(sequenceFeature);
+
+        boolean isContactFeature = sf.isContactFeature();
 
         if (isContactFeature)
         {
-          boolean drawn = renderFeature(g, seq,
-                  seq.findIndex(sequenceFeature.begin) - 1,
-                  seq.findIndex(sequenceFeature.begin) - 1, featureColour,
-                  start, end, y1, colourOnly);
-          drawn |= renderFeature(g, seq,
-                  seq.findIndex(sequenceFeature.end) - 1,
-                  seq.findIndex(sequenceFeature.end) - 1, featureColour,
-                  start, end, y1, colourOnly);
+          boolean drawn = renderFeature(g, seq, featureStartCol - 1,
+                  featureStartCol - 1, featureColour, start, end, y1,
+                  colourOnly);
+          drawn |= renderFeature(g, seq, featureEndCol - 1,
+                  featureEndCol - 1, featureColour, start, end, y1,
+                  colourOnly);
           if (drawn)
           {
             drawnColour = featureColour;
           }
         }
-        else if (showFeature(sequenceFeature))
+        else
         {
           /*
            * showing feature score by height of colour
@@ -347,8 +376,8 @@ public class FeatureRenderer extends FeatureRendererModel
           {
           */
             boolean drawn = renderFeature(g, seq,
-                    seq.findIndex(sequenceFeature.begin) - 1,
-                    seq.findIndex(sequenceFeature.end) - 1, featureColour,
+                    featureStartCol - 1,
+                    featureEndCol - 1, featureColour,
                     start, end, y1, colourOnly);
             if (drawn)
             {
@@ -372,6 +401,27 @@ public class FeatureRenderer extends FeatureRendererModel
   }
 
   /**
+<<<<<<< HEAD
+=======
+   * Answers true if the feature belongs to a feature group which is not
+   * currently displayed, else false
+   * 
+   * @param sequenceFeature
+   * @return
+   */
+  @Override
+  protected boolean featureGroupNotShown(
+          final SequenceFeature sequenceFeature)
+  {
+    return featureGroups != null && sequenceFeature.featureGroup != null
+            && sequenceFeature.featureGroup.length() != 0
+            && featureGroups.containsKey(sequenceFeature.featureGroup)
+            && !featureGroups.get(sequenceFeature.featureGroup)
+                    .booleanValue();
+  }
+
+  /**
+>>>>>>> refs/heads/develop
    * Called when alignment in associated view has new/modified features to
    * discover and display.
    * 
@@ -383,16 +433,22 @@ public class FeatureRenderer extends FeatureRendererModel
   }
 
   /**
-   * Returns the sequence feature colour rendered at the given sequence
-   * position, or null if none found. The feature of highest render order (i.e.
-   * on top) is found, subject to both feature type and feature group being
-   * visible, and its colour returned.
+   * 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
+   * found, subject to both feature type and feature group being visible, and
+   * its colour returned. This method is suitable when no feature transparency
+   * applied (only the topmost visible feature colour is rendered).
+   * <p>
+   * Note this method does not check for a gap in the column so would return the
+   * colour for features enclosing a gapped column. Check for gap before calling
+   * if different behaviour is wanted.
    * 
    * @param seq
-   * @param pos
+   * @param column
+   *          (1..)
    * @return
    */
-  Color findFeatureColour(SequenceI seq, int pos)
+  Color findFeatureColour(SequenceI seq, int column)
   {
     /*
      * check for new feature added while processing
@@ -403,7 +459,8 @@ public class FeatureRenderer extends FeatureRendererModel
      * inspect features in reverse renderOrder (the last in the array is 
      * displayed on top) until we find one that is rendered at the position
      */
-    for (int renderIndex = renderOrder.length - 1; renderIndex >= 0; renderIndex--)
+    for (int renderIndex = renderOrder.length
+            - 1; renderIndex >= 0; renderIndex--)
     {
       String type = renderOrder[renderIndex];
       if (!showFeatureOfType(type))
@@ -411,16 +468,21 @@ public class FeatureRenderer extends FeatureRendererModel
         continue;
       }
 
-      List<SequenceFeature> overlaps = seq.findFeatures(pos, pos, type);
+      List<SequenceFeature> overlaps = seq.findFeatures(column, column,
+              type);
       for (SequenceFeature sequenceFeature : overlaps)
       {
         if (!featureGroupNotShown(sequenceFeature))
         {
-          return getColour(sequenceFeature);
+          Color col = getColour(sequenceFeature);
+          if (col != null)
+          {
+            return col;
+          }
         }
       }
     }
-  
+
     /*
      * no displayed feature found at position
      */