X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Frenderer%2FContactGeometry.java;h=42dde8d07a5e828aee9f20da1fc1ad7975ff128d;hb=4a28fee3e0c129a01268d26e1396b045bd893fb8;hp=0e5107abba20dff67a5eee2f1bad0ddb836987ae;hpb=4cf91ec17ae81e5f4be97aacfb171e70add999c3;p=jalview.git diff --git a/src/jalview/renderer/ContactGeometry.java b/src/jalview/renderer/ContactGeometry.java index 0e5107a..42dde8d 100644 --- a/src/jalview/renderer/ContactGeometry.java +++ b/src/jalview/renderer/ContactGeometry.java @@ -1,22 +1,88 @@ +/* + * 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; - public ContactGeometry(ContactListI contacts, int graphHeight) + /** + * number of pixels to map contact_height to + */ + final 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 = ((double) contact_height) / ((double) graphHeight); + contacts_per_pixel = (graphHeight <= 1) ? contact_height + : ((double) contact_height) / ((double) graphHeight); if (contacts_per_pixel >= 1) { @@ -29,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 @@ -50,24 +119,162 @@ 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); + } + + /** + * return the cell containing given pixel + * + * @param pCentre + * @return range for pCEntre + */ + public contactInterval mapFor(int pCentre) + { + 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(