JAL-2323 render disulfide bond correctly on overview and structure
[jalview.git] / src / jalview / renderer / seqfeatures / FeatureRenderer.java
index cd2a90a..07d97d0 100644 (file)
@@ -1,7 +1,29 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.renderer.seqfeatures;
 
+import jalview.api.AlignViewportI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
 import java.awt.AlphaComposite;
 import java.awt.Color;
@@ -10,8 +32,7 @@ import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 
-public class FeatureRenderer extends
-        jalview.viewmodel.seqfeatures.FeatureRendererModel
+public class FeatureRenderer extends FeatureRendererModel
 {
 
   FontMetrics fm;
@@ -20,28 +41,6 @@ public class FeatureRenderer extends
 
   boolean offscreenRender = false;
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param g
-   *          DOCUMENT ME!
-   * @param seq
-   *          DOCUMENT ME!
-   * @param sg
-   *          DOCUMENT ME!
-   * @param start
-   *          DOCUMENT ME!
-   * @param end
-   *          DOCUMENT ME!
-   * @param x1
-   *          DOCUMENT ME!
-   * @param y1
-   *          DOCUMENT ME!
-   * @param width
-   *          DOCUMENT ME!
-   * @param height
-   *          DOCUMENT ME!
-   */
   protected SequenceI lastSeq;
 
   char s;
@@ -52,6 +51,18 @@ public class FeatureRenderer extends
 
   boolean av_validCharWidth, av_isShowSeqFeatureHeight;
 
+  private Integer currentColour;
+
+  /**
+   * Constructor given a viewport
+   * 
+   * @param viewport
+   */
+  public FeatureRenderer(AlignViewportI viewport)
+  {
+    this.av = viewport;
+  }
+
   protected void updateAvConfig()
   {
     av_charHeight = av.getCharHeight();
@@ -164,56 +175,60 @@ public class FeatureRenderer extends
         charOffset = (av_charWidth - fm.charWidth(s)) / 2;
         g.drawString(String.valueOf(s), charOffset
                 + (av_charWidth * (i - start)), pady);
-
       }
     }
   }
 
   BufferedImage offscreenImage;
 
+  @Override
   public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
   {
     return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
   }
 
   /**
-   * This is used by the Molecule Viewer and Overview to get the accurate
-   * colourof the rendered sequence
+   * This is used by Structure Viewers and the Overview Window to get the
+   * feature colour of the rendered sequence, returned as an RGB value
+   * 
+   * @param defaultColour
+   * @param seq
+   * @param column
+   * @return
    */
-  public synchronized int findFeatureColour(int initialCol, final SequenceI seq,
-          int column)
+  public synchronized int findFeatureColour(int defaultColour,
+          final SequenceI seq, int column)
   {
     if (!av.isShowSequenceFeatures())
     {
-      return initialCol;
+      return defaultColour;
     }
 
-    final SequenceI aseq = (seq.getDatasetSequence() != null) ? seq
-            .getDatasetSequence() : seq;
+    SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
     if (seq != lastSeq)
     {
       lastSeq = seq;
-      sequenceFeatures = aseq.getSequenceFeatures();
-      if (sequenceFeatures != null)
+      lastSequenceFeatures = sequenceFeatures;
+      if (lastSequenceFeatures != null)
       {
-        sfSize = sequenceFeatures.length;
+        sfSize = lastSequenceFeatures.length;
       }
     }
     else
     {
-      if (sequenceFeatures != aseq.getSequenceFeatures())
+      if (lastSequenceFeatures != sequenceFeatures)
       {
-        sequenceFeatures = aseq.getSequenceFeatures();
-        if (sequenceFeatures != null)
+        lastSequenceFeatures = sequenceFeatures;
+        if (lastSequenceFeatures != null)
         {
-          sfSize = sequenceFeatures.length;
+          sfSize = lastSequenceFeatures.length;
         }
       }
     }
 
-    if (sequenceFeatures == null || sfSize == 0)
+    if (lastSequenceFeatures == null || sfSize == 0)
     {
-      return initialCol;
+      return defaultColour;
     }
 
     if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
@@ -234,7 +249,7 @@ public class FeatureRenderer extends
 
     if (offscreenImage != null)
     {
-      offscreenImage.setRGB(0, 0, initialCol);
+      offscreenImage.setRGB(0, 0, defaultColour);
       drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
 
       return offscreenImage.getRGB(0, 0);
@@ -245,17 +260,17 @@ public class FeatureRenderer extends
 
       if (currentColour == null)
       {
-        return initialCol;
+        return defaultColour;
       }
       else
       {
-        return ((Integer) currentColour).intValue();
+        return currentColour.intValue();
       }
     }
 
   }
 
-  private volatile SequenceFeature[] sequenceFeatures;
+  private volatile SequenceFeature[] lastSequenceFeatures;
 
   int sfSize;
 
@@ -265,13 +280,24 @@ public class FeatureRenderer extends
 
   int epos;
 
+  /**
+   * Draws the sequence on the graphics context, or just determines the colour
+   * that would be drawn (if flag offscreenrender is true).
+   * 
+   * @param g
+   * @param seq
+   * @param start
+   *          start column (or sequence position in offscreenrender mode)
+   * @param end
+   *          end column (not used in offscreenrender mode)
+   * @param y1
+   *          vertical offset at which to draw on the graphics
+   */
   public synchronized void drawSequence(Graphics g, final SequenceI seq,
           int start, int end, int y1)
   {
-    final SequenceI aseq = (seq.getDatasetSequence() != null) ? seq
-            .getDatasetSequence() : seq;
-    if (aseq.getSequenceFeatures() == null
-            || aseq.getSequenceFeatures().length == 0)
+    SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
+    if (sequenceFeatures == null || sequenceFeatures.length == 0)
     {
       return;
     }
@@ -284,10 +310,10 @@ public class FeatureRenderer extends
     updateFeatures();
 
     if (lastSeq == null || seq != lastSeq
-            || aseq.getSequenceFeatures() != sequenceFeatures)
+            || sequenceFeatures != lastSequenceFeatures)
     {
       lastSeq = seq;
-      sequenceFeatures = aseq.getSequenceFeatures();
+      lastSequenceFeatures = sequenceFeatures;
     }
 
     if (transparency != 1 && g != null)
@@ -303,13 +329,10 @@ public class FeatureRenderer extends
       epos = lastSeq.findPosition(end);
     }
 
-    sfSize = sequenceFeatures.length;
-    String type;
-    for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
+    sfSize = lastSequenceFeatures.length;
+    for (String type : renderOrder)
     {
-      type = renderOrder[renderIndex];
-
-      if (type == null || !showFeatureOfType(type))
+      if (!showFeatureOfType(type))
       {
         continue;
       }
@@ -318,90 +341,89 @@ public class FeatureRenderer extends
       // current feature to render
       for (sfindex = 0; sfindex < sfSize; sfindex++)
       {
-        if (!sequenceFeatures[sfindex].type.equals(type))
+        final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
+        if (!sequenceFeature.type.equals(type))
         {
           continue;
         }
 
-        if (featureGroups != null
-                && sequenceFeatures[sfindex].featureGroup != null
-                && sequenceFeatures[sfindex].featureGroup.length() != 0
-                && featureGroups
-                        .containsKey(sequenceFeatures[sfindex].featureGroup)
-                && !((Boolean) featureGroups
-                        .get(sequenceFeatures[sfindex].featureGroup))
-                        .booleanValue())
+        if (featureGroupNotShown(sequenceFeature))
         {
           continue;
         }
 
+        /*
+         * check feature overlaps the visible part of the alignment, 
+         * unless doing offscreenRender (to the Overview window or a 
+         * structure viewer) which is not limited 
+         */
         if (!offscreenRender
-                && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
+                && (sequenceFeature.getBegin() > epos || sequenceFeature
                         .getEnd() < spos))
         {
           continue;
         }
 
+        Color featureColour = getColour(sequenceFeature);
+        boolean isDisulfideBond = "disulfide bond".equals(sequenceFeature.type);
         if (offscreenRender && offscreenImage == null)
         {
-          if (sequenceFeatures[sfindex].begin <= start
-                  && sequenceFeatures[sfindex].end >= start)
+          /*
+           * offscreen mode with no image (image is only needed if transparency 
+           * is applied to feature colours) - just check feature is rendered at 
+           * the requested position (start == sequence position in this mode)
+           */
+          boolean featureIsAtPosition = sequenceFeature.begin <= start
+                  && sequenceFeature.end >= start;
+          if (isDisulfideBond)
+          {
+            featureIsAtPosition = sequenceFeature.begin == start
+                    || sequenceFeature.end == start;
+          }
+          if (featureIsAtPosition)
           {
             // this is passed out to the overview and other sequence renderers
             // (e.g. molecule viewer) to get displayed colour for rendered
             // sequence
-            currentColour = new Integer(
-                    getColour(sequenceFeatures[sfindex]).getRGB());
+            currentColour = new Integer(featureColour.getRGB());
             // used to be retreived from av.featuresDisplayed
             // currentColour = av.featuresDisplayed
             // .get(sequenceFeatures[sfindex].type);
 
           }
         }
-        else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
+        else if (isDisulfideBond)
         {
-
-          renderFeature(g, seq,
-                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                  getColour(sequenceFeatures[sfindex])
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
-          renderFeature(g, seq,
-                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                  getColour(sequenceFeatures[sfindex])
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
+          renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
+                  seq.findIndex(sequenceFeature.begin) - 1, featureColour,
+                  start, end, y1);
+          renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
+                  seq.findIndex(sequenceFeature.end) - 1, featureColour,
+                  start, end, y1);
 
         }
-        else if (showFeature(sequenceFeatures[sfindex]))
+        else if (showFeature(sequenceFeature))
         {
           if (av_isShowSeqFeatureHeight
-                  && sequenceFeatures[sfindex].score != Float.NaN)
+                  && !Float.isNaN(sequenceFeature.score))
           {
             renderScoreFeature(g, seq,
-                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                    getColour(sequenceFeatures[sfindex]), start, end, y1,
-                    normaliseScore(sequenceFeatures[sfindex]));
+                    seq.findIndex(sequenceFeature.begin) - 1,
+                    seq.findIndex(sequenceFeature.end) - 1,
+                    featureColour, start, end, y1,
+                    normaliseScore(sequenceFeature));
           }
           else
           {
-            renderFeature(g, seq,
-                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
-                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
-                    getColour(sequenceFeatures[sfindex]), start, end, y1);
+            renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
+                    seq.findIndex(sequenceFeature.end) - 1,
+                    featureColour, start, end, y1);
           }
         }
-
       }
-
     }
 
-    if (transparency != 1.0f && g != null && transparencyAvailable)
+    if (transparency != 1.0f && g != null)
     {
       Graphics2D g2 = (Graphics2D) g;
       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
@@ -409,17 +431,22 @@ public class FeatureRenderer extends
     }
   }
 
-  boolean transparencyAvailable = true;
-
-  protected void setTransparencyAvailable(boolean isTransparencyAvailable)
-  {
-    transparencyAvailable = isTransparencyAvailable;
-  }
-
-  @Override
-  public boolean isTransparencyAvailable()
+  /**
+   * Answers true if the feature belongs to a feature group which is not
+   * currently displayed, else false
+   * 
+   * @param sequenceFeature
+   * @return
+   */
+  protected boolean featureGroupNotShown(
+          final SequenceFeature sequenceFeature)
   {
-    return transparencyAvailable;
+    return featureGroups != null
+            && sequenceFeature.featureGroup != null
+            && sequenceFeature.featureGroup.length() != 0
+            && featureGroups.containsKey(sequenceFeature.featureGroup)
+            && !featureGroups.get(sequenceFeature.featureGroup)
+                    .booleanValue();
   }
 
   /**
@@ -427,6 +454,7 @@ public class FeatureRenderer extends
    * discover and display.
    * 
    */
+  @Override
   public void featuresAdded()
   {
     lastSeq = null;