X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Frenderer%2FContactGeometry.java;h=42dde8d07a5e828aee9f20da1fc1ad7975ff128d;hb=164fb77632cc3c9a3b5f5ac5290eadec920ffbc6;hp=4aef1d81ae9bdd95fa86234cedf12025ad323020;hpb=e70614203293e8f04d7640729851725a763ae130;p=jalview.git
diff --git a/src/jalview/renderer/ContactGeometry.java b/src/jalview/renderer/ContactGeometry.java
index 4aef1d8..42dde8d 100644
--- a/src/jalview/renderer/ContactGeometry.java
+++ b/src/jalview/renderer/ContactGeometry.java
@@ -1,25 +1,87 @@
+/*
+ * 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 .
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.renderer;
+import java.util.Arrays;
import java.util.Iterator;
+import java.util.List;
+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;
+
+ /**
+ * how many pixels per contact (1..many)
+ */
final int pixels_step;
+ /**
+ * how many contacts per pixel (many > 0)
+ */
final double contacts_per_pixel;
+ /**
+ * number of contacts being mapped
+ */
final int contact_height;
+ /**
+ * number of pixels to map contact_height to
+ */
final int graphHeight;
- public ContactGeometry(ContactListI contacts, int graphHeight)
+ /**
+ * number of contacts for each pixel_step - to last whole contact
+ */
+ final double contacts_step;
+
+ final int lastStep;
+
+ /**
+ * Bean used to map from a range of contacts to a range of pixels
+ *
+ * @param contacts
+ * @param graphHeight
+ * Number of pixels to map given range of contacts
+ */
+ 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 = (graphHeight < 1) ? contact_height
+ contacts_per_pixel = (graphHeight <= 1) ? contact_height
: ((double) contact_height) / ((double) graphHeight);
if (contacts_per_pixel >= 1)
@@ -33,6 +95,9 @@ public class ContactGeometry
pixels_step = (int) Math
.ceil(((double) graphHeight) / (double) contact_height);
}
+ contacts_step = pixels_step * contacts_per_pixel;
+ lastStep = (int) Math.min((double) graphHeight,
+ ((double) graphHeight) / ((double) pixels_step));
}
public class contactInterval
@@ -54,24 +119,127 @@ public class ContactGeometry
public final int pStart;
public final int pEnd;
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null || !(obj instanceof contactInterval))
+ {
+ return false;
+ }
+ contactInterval them = (contactInterval) obj;
+ return cStart == them.cStart && cEnd == them.cEnd && pEnd == them.pEnd
+ && pStart == them.pStart;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Contacts [" + cStart + "," + cEnd + "] : Pixels [" + pStart
+ + "," + 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 viscont = hiddenColumns.getVisContigsIterator(
+ -1 + mappedRange[p], -1 + 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(-1 + mappedRange[p],
+ -1 + mappedRange[p + 1]);
+ }
+ }
+ return rowsel;
+
+ }
+
+ /**
+ * Return mapped cell intersecting pStart \
+ *
+ * FIXME: REDUNDANT METHOD - COULD DELETE FIXME: OR RE-IMPLEMENT AS EFFICIENT
+ * RANGE QUERY
+ *
* @param pStart
+ * [0..)
* @param pEnd
- * @return range for
+ * @return nearest full cell containing pStart - does not set
+ * contactInterval.pEnd or cEnd to equivalent position on pEnd !
*/
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;
+ if (pStart < 0)
+ {
+ pStart = 0;
+ }
+ if (pEnd < pStart)
+ {
+ pEnd = pStart;
+ }
+ if (pEnd >= graphHeight)
+ {
+ pEnd = graphHeight - 1;
+ }
+ if (pStart >= graphHeight)
+ {
+ pStart = graphHeight - pixels_step;
+ }
+ int step = Math.floorDiv(pStart, pixels_step);
+ return findStep(step);
+ }
+
+ /**
+ *
+ * @param step
+ * [0..) n steps covering height and contactHeight
+ * @return contactInterval for step, or null if out of bounds
+ */
+ contactInterval findStep(int step)
+ {
+ if (step < 0 || step > lastStep)
+ {
+ return null;
+ }
+ return new contactInterval((int) Math.floor(contacts_step * step),
+ -1 + (int) Math.min(contact_height,
+ Math.floor(contacts_step * (step + 1))),
+ pixels_step * step,
+ Math.min(graphHeight, (step + 1) * pixels_step) - 1);
}
/**
@@ -82,15 +250,31 @@ public class ContactGeometry
*/
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;
+ if (pCentre >= graphHeight + pixels_step)
+ {
+ return null;
+ }
+ int step = Math.floorDiv(pCentre, pixels_step);
+ return findStep(step);
+ }
+
+ public List allSteps()
+ {
+ contactInterval[] array = new contactInterval[lastStep + 1];
+ int csum = 0, psum = 0;
+ for (int i = 0; i <= lastStep; i++)
+ {
+ array[i] = findStep(i);
+ csum += 1 + array[i].cEnd - array[i].cStart;
+ psum += 1 + array[i].pEnd - array[i].pStart;
+ }
+ if (csum != contact_height || psum != graphHeight)
+ {
+ System.err.println("csum = " + csum + " not " + contact_height + "\n"
+ + "psum = " + psum + " not " + graphHeight);
+ return null;
+ }
+ return Arrays.asList(array);
}
public Iterator iterateOverContactIntervals(