JAL-3612 use _last_ drawn colour per feature type in Overview
[jalview.git] / src / jalview / renderer / seqfeatures / FeatureRenderer.java
index 13885b4..fe80e63 100644 (file)
  */
 package jalview.renderer.seqfeatures;
 
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.util.List;
+
 import jalview.api.AlignViewportI;
 import jalview.api.FeatureColourI;
 import jalview.datamodel.ContiguousI;
+import jalview.datamodel.MappedFeatures;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
 import jalview.util.Comparison;
+import jalview.util.ReverseListIterator;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
-import java.awt.AlphaComposite;
-import java.awt.Color;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.util.List;
-
 public class FeatureRenderer extends FeatureRendererModel
 {
   private static final AlphaComposite NO_TRANSPARENCY = AlphaComposite
@@ -108,6 +112,10 @@ public class FeatureRenderer extends FeatureRendererModel
         continue;
       }
 
+      /*
+       * JAL-3045 text is always drawn over features, even if
+       * 'Show Text' is unchecked in the format menu
+       */
       g.setColor(Color.white);
       int charOffset = (charWidth - fm.charWidth(s)) / 2;
       g.drawString(String.valueOf(s),
@@ -273,7 +281,8 @@ public class FeatureRenderer extends FeatureRendererModel
      * if columns are all gapped, or sequence has no features, nothing to do
      */
     ContiguousI visiblePositions = seq.findPositions(start + 1, end + 1);
-    if (visiblePositions == null || !seq.getFeatures().hasFeatures())
+    if (visiblePositions == null || !seq.getFeatures().hasFeatures()
+            && !av.isShowComplementFeatures())
     {
       return null;
     }
@@ -290,6 +299,16 @@ public class FeatureRenderer extends FeatureRendererModel
     Color drawnColour = null;
 
     /*
+     * draw 'complement' features below ours if configured to do so
+     */
+    if (av.isShowComplementFeatures()
+            && !av.isShowComplementFeaturesOnTop())
+    {
+      drawnColour = drawComplementFeatures(g, seq, start, end, y1,
+              colourOnly, visiblePositions, drawnColour);
+    }
+
+    /*
      * iterate over features in ordering of their rendering (last is on top)
      */
     for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
@@ -304,7 +323,7 @@ public class FeatureRenderer extends FeatureRendererModel
       List<SequenceFeature> overlaps = seq.getFeatures().findFeatures(
               visiblePositions.getBegin(), visiblePositions.getEnd(), type);
 
-      if (fc.isSimpleColour())
+      if (overlaps.size() > 1 && fc.isSimpleColour())
       {
         filterFeaturesForDisplay(overlaps);
       }
@@ -315,7 +334,7 @@ public class FeatureRenderer extends FeatureRendererModel
         if (featureColour == null)
         {
           /*
-           * feature excluded by visibility settings, filters, or colour threshold
+           * feature excluded by filters, or colour threshold
            */
           continue;
         }
@@ -394,6 +413,15 @@ public class FeatureRenderer extends FeatureRendererModel
       }
     }
 
+    /*
+     * draw 'complement' features above ours if configured to do so
+     */
+    if (av.isShowComplementFeatures() && av.isShowComplementFeaturesOnTop())
+    {
+      drawnColour = drawComplementFeatures(g, seq, start, end, y1,
+              colourOnly, visiblePositions, drawnColour);
+    }
+
     if (transparency != 1.0f && g != null)
     {
       /*
@@ -407,6 +435,52 @@ public class FeatureRenderer extends FeatureRendererModel
   }
 
   /**
+   * Find any features on the CDS/protein complement of the sequence region and
+   * draw them, with visibility and colouring as configured in the complementary
+   * viewport
+   * 
+   * @param g
+   * @param seq
+   * @param start
+   * @param end
+   * @param y1
+   * @param colourOnly
+   * @param visiblePositions
+   * @param drawnColour
+   * @return
+   */
+  Color drawComplementFeatures(final Graphics g, final SequenceI seq,
+          int start, int end, int y1, boolean colourOnly,
+          ContiguousI visiblePositions, Color drawnColour)
+  {
+    AlignViewportI comp = av.getCodingComplement();
+    FeatureRenderer fr2 = Desktop.getAlignFrameFor(comp)
+            .getFeatureRenderer();
+
+    final int visibleStart = visiblePositions.getBegin();
+    final int visibleEnd = visiblePositions.getEnd();
+
+    for (int pos = visibleStart; pos <= visibleEnd; pos++)
+    {
+      int column = seq.findIndex(pos);
+      MappedFeatures mf = fr2
+              .findComplementFeaturesAtResidue(seq, pos);
+      if (mf != null)
+      {
+        for (SequenceFeature sf : mf.features)
+        {
+          FeatureColourI fc = fr2.getFeatureStyle(sf.getType());
+          Color featureColour = fr2.getColor(sf, fc);
+          renderFeature(g, seq, column - 1, column - 1, featureColour,
+                  start, end, y1, colourOnly);
+          drawnColour = featureColour;
+        }
+      }
+    }
+    return drawnColour;
+  }
+
+  /**
    * Called when alignment in associated view has new/modified features to
    * discover and display.
    * 
@@ -441,6 +515,18 @@ public class FeatureRenderer extends FeatureRendererModel
     updateFeatures();
 
     /*
+     * show complement features on top (if configured to show them)
+     */
+    if (av.isShowComplementFeatures() && av.isShowComplementFeaturesOnTop())
+    {
+      Color col = findComplementFeatureColour(seq, column);
+      if (col != null)
+      {
+        return col;
+      }
+    }
+
+    /*
      * inspect features in reverse renderOrder (the last in the array is 
      * displayed on top) until we find one that is rendered at the position
      */
@@ -453,10 +539,15 @@ public class FeatureRenderer extends FeatureRendererModel
         continue;
       }
 
+      /*
+       * find features of this type, and the colour of the _last_ one
+       * (the one that would be drawn on top) that has a colour
+       */
       List<SequenceFeature> overlaps = seq.findFeatures(column, column,
               type);
-      for (SequenceFeature sequenceFeature : overlaps)
+      for (int i = overlaps.size() - 1 ; i >= 0 ; i--)
       {
+        SequenceFeature sequenceFeature = overlaps.get(i);
         if (!featureGroupNotShown(sequenceFeature))
         {
           Color col = getColour(sequenceFeature);
@@ -469,8 +560,43 @@ public class FeatureRenderer extends FeatureRendererModel
     }
 
     /*
-     * no displayed feature found at position
+     * show complement features underneath (if configured to show them)
      */
+    Color col = null;
+    if (av.isShowComplementFeatures()
+            && !av.isShowComplementFeaturesOnTop())
+    {
+      col = findComplementFeatureColour(seq, column);
+    }
+
+    return col;
+  }
+
+  Color findComplementFeatureColour(SequenceI seq, int column)
+  {
+    AlignViewportI complement = av.getCodingComplement();
+    AlignFrame af = Desktop.getAlignFrameFor(complement);
+    FeatureRendererModel fr2 = af.getFeatureRenderer();
+    MappedFeatures mf = fr2.findComplementFeaturesAtResidue(
+            seq, seq.findPosition(column - 1));
+    if (mf == null)
+    {
+      return null;
+    }
+    ReverseListIterator<SequenceFeature> it = new ReverseListIterator<>(
+            mf.features);
+    while (it.hasNext())
+    {
+      SequenceFeature sf = it.next();
+      if (!fr2.featureGroupNotShown(sf))
+      {
+        Color col = fr2.getColour(sf);
+        if (col != null)
+        {
+          return col;
+        }
+      }
+    }
     return null;
   }
 }