X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Frenderer%2FContactGeometry.java;h=a1443c82cec29f357abfe974f60cf85a24821eab;hb=cd669a0e8c7b91b379bca8fe6e702cf0fcbd1ce0;hp=4aef1d81ae9bdd95fa86234cedf12025ad323020;hpb=e70614203293e8f04d7640729851725a763ae130;p=jalview.git diff --git a/src/jalview/renderer/ContactGeometry.java b/src/jalview/renderer/ContactGeometry.java index 4aef1d8..a1443c8 100644 --- a/src/jalview/renderer/ContactGeometry.java +++ b/src/jalview/renderer/ContactGeometry.java @@ -1,25 +1,65 @@ 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 +73,8 @@ 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 +96,125 @@ 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 +225,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(