Merge branch 'develop' into features/JAL-4134_use_annotation_row_for_colours_and_groups
[jalview.git] / src / jalview / renderer / ContactGeometry.java
index 0e5107a..9fd4de6 100644 (file)
@@ -2,21 +2,39 @@ package jalview.renderer;
 
 import java.util.Iterator;
 
+import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.ContactListI;
+import jalview.datamodel.HiddenColumns;
+import jalview.renderer.ContactGeometry.contactInterval;
 
+/**
+ * encapsulate logic for mapping between positions in a ContactList and their
+ * rendered representation in a given number of pixels.
+ * 
+ * @author jprocter
+ *
+ */
 public class ContactGeometry
 {
+
+  final ContactListI contacts;
+
   final int pixels_step;
 
   final double contacts_per_pixel;
 
   final int contact_height;
 
-  public ContactGeometry(ContactListI contacts, int graphHeight)
+  final int graphHeight;
+
+  public ContactGeometry(final ContactListI contacts, int graphHeight)
   {
+    this.contacts = contacts;
+    this.graphHeight = graphHeight;
     contact_height = contacts.getContactHeight();
     // fractional number of contacts covering each pixel
-    contacts_per_pixel = ((double) contact_height) / ((double) graphHeight);
+    contacts_per_pixel = (graphHeight < 1) ? contact_height
+            : ((double) contact_height) / ((double) graphHeight);
 
     if (contacts_per_pixel >= 1)
     {
@@ -50,6 +68,56 @@ public class ContactGeometry
     public final int pStart;
 
     public final int pEnd;
+
+  }
+
+  /**
+   * 
+   * @param columnSelection
+   * @param ci
+   * @param visibleOnly
+   *          - when true, only test intersection of visible columns given
+   *          matrix range
+   * @return true if the range on the matrix specified by ci intersects with
+   *         selected columns in the ContactListI's reference frame.
+   */
+
+  boolean intersects(contactInterval ci, ColumnSelection columnSelection,
+          HiddenColumns hiddenColumns, boolean visibleOnly)
+  {
+    boolean rowsel = false;
+    final int[] mappedRange = contacts.getMappedPositionsFor(ci.cStart,
+            ci.cEnd);
+    if (mappedRange == null)
+    {
+      return false;
+    }
+    for (int p = 0; p < mappedRange.length && !rowsel; p += 2)
+    {
+      boolean containsHidden = false;
+      if (visibleOnly && hiddenColumns != null
+              && hiddenColumns.hasHiddenColumns())
+      {
+        // TODO: turn into function on hiddenColumns and create test !!
+        Iterator<int[]> viscont = hiddenColumns.getVisContigsIterator(
+                mappedRange[p], mappedRange[p + 1], false);
+        containsHidden = !viscont.hasNext();
+        if (!containsHidden)
+        {
+          for (int[] interval = viscont.next(); viscont
+                  .hasNext(); rowsel |= columnSelection
+                          .intersects(interval[p], interval[p + 1]))
+            ;
+        }
+      }
+      else
+      {
+        rowsel = columnSelection.intersects(mappedRange[p],
+                mappedRange[p + 1]);
+      }
+    }
+    return rowsel;
+
   }
 
   /**
@@ -70,6 +138,25 @@ public class ContactGeometry
     return ci;
   }
 
+  /**
+   * return the cell containing given pixel
+   * 
+   * @param pCentre
+   * @return range for pCEntre
+   */
+  public contactInterval mapFor(int pCentre)
+  {
+    int pStart = Math.max(pCentre - pixels_step, 0);
+    int pEnd = Math.min(pStart + pixels_step, graphHeight);
+    int cStart = (int) Math.floor(pStart * contacts_per_pixel);
+    contactInterval ci = new contactInterval(cStart,
+            (int) Math.min(contact_height,
+                    Math.ceil(cStart + (pixels_step) * contacts_per_pixel)),
+            pStart, pEnd);
+
+    return ci;
+  }
+
   public Iterator<contactInterval> iterateOverContactIntervals(
           int graphHeight)
   {