JAL-2349 JAL-4033 refactored contact matrix geom calc to allow reporting of mapped...
authorJim Procter <j.procter@dundee.ac.uk>
Wed, 19 Oct 2022 12:10:58 +0000 (13:10 +0100)
committerJim Procter <j.procter@dundee.ac.uk>
Wed, 19 Oct 2022 12:10:58 +0000 (13:10 +0100)
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/SeqPanel.java
src/jalview/renderer/ContactGeometry.java [new file with mode: 0644]
src/jalview/renderer/ContactMapRenderer.java

index 7c28bd0..d80c749 100755 (executable)
@@ -50,15 +50,18 @@ import javax.swing.JPopupMenu;
 import javax.swing.Scrollable;
 import javax.swing.ToolTipManager;
 
+import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.ContactListI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SequenceI;
 import jalview.gui.JalviewColourChooser.ColourChooserListener;
 import jalview.renderer.AnnotationRenderer;
 import jalview.renderer.AwtRenderPanelI;
+import jalview.renderer.ContactGeometry;
 import jalview.schemes.ResidueProperties;
 import jalview.util.Comparison;
 import jalview.util.MessageManager;
@@ -778,8 +781,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   {
     int yPos = evt.getY();
     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
-
-    int row = getRowIndex(yPos, aa);
+    int rowAndOffset[] = getRowIndexAndOffset(yPos, aa);
+    int row = rowAndOffset[0];
 
     if (row == -1)
     {
@@ -801,10 +804,11 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     if (row > -1 && ann.annotations != null
             && column < ann.annotations.length)
     {
-      String toolTip = buildToolTip(ann, column, aa);
+      String toolTip = buildToolTip(ann, column, aa, rowAndOffset[1], av);
       setToolTipText(toolTip == null ? null
               : JvSwingUtils.wrapTooltip(true, toolTip));
-      String msg = getStatusMessage(av.getAlignment(), column, ann);
+      String msg = getStatusMessage(av.getAlignment(), column, ann,
+              rowAndOffset[1], av);
       ap.alignFrame.setStatus(msg);
     }
     else
@@ -830,23 +834,38 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     {
       return -1;
     }
+    return getRowIndexAndOffset(yPos, aa)[0];
+  }
+
+  static int[] getRowIndexAndOffset(int yPos, AlignmentAnnotation[] aa)
+  {
+    int[] res = new int[2];
+    if (aa == null)
+    {
+      res[0] = -1;
+      res[1] = 0;
+      return res;
+    }
     int row = -1;
-    int height = 0;
+    int height = 0, lheight = 0;
 
     for (int i = 0; i < aa.length; i++)
     {
       if (aa[i].visible)
       {
+        lheight = height;
         height += aa[i].height;
       }
 
       if (height > yPos)
       {
         row = i;
+        res[0] = row;
+        res[1] = yPos - lheight;
         break;
       }
     }
-    return row;
+    return res;
   }
 
   /**
@@ -857,9 +876,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
    * @param ann
    * @param column
    * @param anns
+   * @param rowAndOffset
    */
   static String buildToolTip(AlignmentAnnotation ann, int column,
-          AlignmentAnnotation[] anns)
+          AlignmentAnnotation[] anns, int rowAndOffset, AlignViewportI av)
   {
     String tooltip = null;
     if (ann.graphGroup > -1)
@@ -891,7 +911,18 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     {
       tooltip = ann.annotations[column].description;
     }
-
+    // TODO abstract tooltip generator so different implementations can be built
+    if (ann.graph == AlignmentAnnotation.CUSTOMRENDERER)
+    {
+      ContactListI clist = av.getContactList(ann, column);
+      if (clist != null)
+      {
+        ContactGeometry cgeom = new ContactGeometry(clist, ann.graphHeight);
+        ContactGeometry.contactInterval ci = cgeom.mapFor(rowAndOffset,
+                rowAndOffset);
+        tooltip += "Contact from " + ci.cStart + " to " + ci.cEnd;
+      }
+    }
     return tooltip;
   }
 
@@ -901,9 +932,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
    * @param al
    * @param column
    * @param ann
+   * @param rowAndOffset
    */
   static String getStatusMessage(AlignmentI al, int column,
-          AlignmentAnnotation ann)
+          AlignmentAnnotation ann, int rowAndOffset, AlignViewportI av)
   {
     /*
      * show alignment column and annotation description if any
@@ -1124,11 +1156,12 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
     Graphics2D gg = (Graphics2D) image.getGraphics();
 
-    if (imgWidth>Math.abs(horizontal*av.getCharWidth())) {
-      //scroll is less than imgWidth away so can re-use buffered graphics
+    if (imgWidth > Math.abs(horizontal * av.getCharWidth()))
+    {
+      // scroll is less than imgWidth away so can re-use buffered graphics
       gg.copyArea(0, 0, imgWidth, getHeight(),
               -horizontal * av.getCharWidth(), 0);
-      
+
       if (horizontal > 0) // scrollbar pulled right, image to the left
       {
         transX = (er - sr - horizontal) * av.getCharWidth();
index 111b4c0..6918811 100644 (file)
@@ -1175,6 +1175,7 @@ public class SeqPanel extends JPanel
     final int column = pos.column;
     final int rowIndex = pos.annotationIndex;
 
+    // TODO - get yOffset for annotation, too
     if (column < 0 || !av.getWrapAlignment() || !av.isShowAnnotation()
             || rowIndex < 0)
     {
@@ -1183,7 +1184,7 @@ public class SeqPanel extends JPanel
     AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
 
     String tooltip = AnnotationPanel.buildToolTip(anns[rowIndex], column,
-            anns);
+            anns, 0, av);
     if (tooltip == null ? tooltip != lastTooltip
             : !tooltip.equals(lastTooltip))
     {
@@ -1194,7 +1195,7 @@ public class SeqPanel extends JPanel
     }
 
     String msg = AnnotationPanel.getStatusMessage(av.getAlignment(), column,
-            anns[rowIndex]);
+            anns[rowIndex], 0, av);
     ap.alignFrame.setStatus(msg);
   }
 
diff --git a/src/jalview/renderer/ContactGeometry.java b/src/jalview/renderer/ContactGeometry.java
new file mode 100644 (file)
index 0000000..0e5107a
--- /dev/null
@@ -0,0 +1,105 @@
+package jalview.renderer;
+
+import java.util.Iterator;
+
+import jalview.datamodel.ContactListI;
+
+public class ContactGeometry
+{
+  final int pixels_step;
+
+  final double contacts_per_pixel;
+
+  final int contact_height;
+
+  public ContactGeometry(ContactListI contacts, int graphHeight)
+  {
+    contact_height = contacts.getContactHeight();
+    // fractional number of contacts covering each pixel
+    contacts_per_pixel = ((double) contact_height) / ((double) graphHeight);
+
+    if (contacts_per_pixel >= 1)
+    {
+      // many contacts rendered per pixel
+      pixels_step = 1;
+    }
+    else
+    {
+      // pixel height for each contact
+      pixels_step = (int) Math
+              .ceil(((double) graphHeight) / (double) contact_height);
+    }
+  }
+
+  public class contactInterval
+  {
+    public contactInterval(int cStart, int cEnd, int pStart, int pEnd)
+    {
+      this.cStart = cStart;
+      this.cEnd = cEnd;
+      this.pStart = pStart;
+      this.pEnd = pEnd;
+    }
+
+    // range on contact list
+    public final int cStart;
+
+    public final int cEnd;
+
+    // range in pixels
+    public final int pStart;
+
+    public final int pEnd;
+  }
+
+  /**
+   * 
+   * @param pStart
+   * @param pEnd
+   * @return range for
+   */
+  public contactInterval mapFor(int pStart, int pEnd)
+  {
+    int cStart = (int) Math.floor(pStart * contacts_per_pixel);
+    contactInterval ci = new contactInterval(cStart,
+            (int) Math.min(contact_height,
+                    Math.ceil(
+                            cStart + (pEnd - pStart) * contacts_per_pixel)),
+            pStart, pEnd);
+
+    return ci;
+  }
+
+  public Iterator<contactInterval> iterateOverContactIntervals(
+          int graphHeight)
+  {
+    // NOT YET IMPLEMENTED
+    return null;
+    // int cstart = 0, cend;
+    //
+    // for (int ht = y2,
+    // eht = y2 - graphHeight; ht >= eht; ht -= pixels_step)
+    // {
+    // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
+    // cend = (int) Math.min(contact_height,
+    // Math.ceil(cstart + contacts_per_pixel * pixels_step));
+    //
+    // return new Iterator<contactIntervals>() {
+    //
+    // @Override
+    // public boolean hasNext()
+    // {
+    // // TODO Auto-generated method stub
+    // return false;
+    // }
+    //
+    // @Override
+    // public contactIntervals next()
+    // {
+    // // TODO Auto-generated method stub
+    // return null;
+    // }
+    //
+    // }
+  }
+}
\ No newline at end of file
index f5c4c31..7a435c3 100644 (file)
@@ -77,33 +77,18 @@ public class ContactMapRenderer implements AnnotationRowRendererI
       {
         return;
       }
-      int contact_height = contacts.getContactHeight();
-      // fractional number of contacts covering each pixel
-      double contacts_per_pixel = ((double) contact_height)
-              / ((double) _aa.graphHeight);
+      // Bean holding mapping from contact list to pixels
+      final ContactGeometry cgeom = new ContactGeometry(contacts,
+              _aa.graphHeight);
 
-      int pixels_step;
-
-      if (contacts_per_pixel >= 1)
-      {
-        // many contacts rendered per pixel
-        pixels_step = 1;
-      }
-      else
-      {
-        // pixel height for each contact
-        pixels_step = (int) Math
-                .ceil(((double) _aa.graphHeight) / (double) contact_height);
-      }
-
-      int cstart = 0, cend;
-
-      for (int ht = y2,
-              eht = y2 - _aa.graphHeight; ht >= eht; ht -= pixels_step)
+      for (int ht = y2, eht = y2
+              - _aa.graphHeight; ht >= eht; ht -= cgeom.pixels_step)
       {
-        cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
-        cend = (int) Math.min(contact_height,
-                Math.ceil(cstart + contacts_per_pixel * pixels_step));
+        ContactGeometry.contactInterval ci = cgeom.mapFor(y2 - ht,
+                y2 - ht + cgeom.pixels_step);
+        // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
+        // cend = (int) Math.min(contact_height,
+        // Math.ceil(cstart + contacts_per_pixel * pixels_step));
 
         // TODO show maximum colour for range - sort of done
         // also need a 'getMaxPosForRange(start,end)' to accurately render
@@ -113,14 +98,14 @@ public class ContactMapRenderer implements AnnotationRowRendererI
         {
           if (_aa.sequenceRef == null)
           {
-            rowsel = columnSelection.intersects(cstart, cend);
+            rowsel = columnSelection.intersects(ci.cStart, ci.cEnd);
           }
           else
           {
             // TODO check we have correctly mapped cstart to local sequence
             // numbering
-            int s = _aa.sequenceRef.findIndex(cstart);
-            int e = _aa.sequenceRef.findIndex(cend);
+            int s = _aa.sequenceRef.findIndex(ci.cStart);
+            int e = _aa.sequenceRef.findIndex(ci.cEnd);
             if (s > 0 && s < _aa.sequenceRef.getLength())
             {
               rowsel = columnSelection.intersects(s, e);
@@ -131,17 +116,18 @@ public class ContactMapRenderer implements AnnotationRowRendererI
         if (colsel || rowsel)
         {
 
-          col = getSelectedColorForRange(min, max, contacts, cstart, cend);
+          col = getSelectedColorForRange(min, max, contacts, ci.cStart,
+                  ci.cEnd);
           g.setColor(col);
         }
         else
         {
-          col = getColorForRange(min, max, contacts, cstart, cend);
+          col = getColorForRange(min, max, contacts, ci.cStart, ci.cEnd);
           g.setColor(col);
         }
-        if (pixels_step > 1)
+        if (cgeom.pixels_step > 1)
         {
-          g.fillRect(x * charWidth, ht, charWidth, 1 + pixels_step);
+          g.fillRect(x * charWidth, ht, charWidth, 1 + cgeom.pixels_step);
         }
         else
         {