1 package jalview.renderer;
3 import java.util.Arrays;
4 import java.util.Iterator;
7 import jalview.datamodel.ColumnSelection;
8 import jalview.datamodel.ContactListI;
9 import jalview.datamodel.HiddenColumns;
10 import jalview.renderer.ContactGeometry.contactInterval;
13 * encapsulate logic for mapping between positions in a ContactList and their
14 * rendered representation in a given number of pixels.
19 public class ContactGeometry
22 final ContactListI contacts;
25 * how many pixels per contact (1..many)
27 final int pixels_step;
30 * how many contacts per pixel (many > 0)
32 final double contacts_per_pixel;
35 * number of contacts being mapped
37 final int contact_height;
40 * number of pixels to map contact_height to
42 final int graphHeight;
45 * number of contacts for each pixel_step - to last whole contact
47 final double contacts_step;
52 * Bean used to map from a range of contacts to a range of pixels
54 * @param graphHeight Number of pixels to map given range of contacts
56 public ContactGeometry(final ContactListI contacts, int graphHeight)
58 this.contacts = contacts;
59 this.graphHeight = graphHeight;
60 contact_height = contacts.getContactHeight();
61 // fractional number of contacts covering each pixel
62 contacts_per_pixel = (graphHeight <= 1) ? contact_height
63 : ((double) contact_height) / ((double) graphHeight);
65 if (contacts_per_pixel >= 1)
67 // many contacts rendered per pixel
72 // pixel height for each contact
73 pixels_step = (int) Math
74 .ceil(((double) graphHeight) / (double) contact_height);
76 contacts_step = pixels_step*contacts_per_pixel;
77 lastStep = (int) Math.min((double)graphHeight, ((double)graphHeight)/((double)pixels_step));
80 public class contactInterval
82 public contactInterval(int cStart, int cEnd, int pStart, int pEnd)
90 // range on contact list
91 public final int cStart;
93 public final int cEnd;
96 public final int pStart;
98 public final int pEnd;
101 public boolean equals(Object obj)
103 if (obj == null || !(obj instanceof contactInterval))
107 contactInterval them = (contactInterval) obj;
108 return cStart == them.cStart && cEnd == them.cEnd && pEnd == them.pEnd
109 && pStart == them.pStart;
112 public String toString()
114 return "Contacts ["+cStart+","+cEnd+"] : Pixels ["+pStart+","+pEnd+"]";
120 * @param columnSelection
123 * - when true, only test intersection of visible columns given
125 * @return true if the range on the matrix specified by ci intersects with
126 * selected columns in the ContactListI's reference frame.
129 boolean intersects(contactInterval ci, ColumnSelection columnSelection,
130 HiddenColumns hiddenColumns, boolean visibleOnly)
132 boolean rowsel = false;
133 final int[] mappedRange = contacts.getMappedPositionsFor(ci.cStart,
135 if (mappedRange == null)
139 for (int p = 0; p < mappedRange.length && !rowsel; p += 2)
141 boolean containsHidden = false;
142 if (visibleOnly && hiddenColumns != null
143 && hiddenColumns.hasHiddenColumns())
145 // TODO: turn into function on hiddenColumns and create test !!
146 Iterator<int[]> viscont = hiddenColumns.getVisContigsIterator(
147 -1+mappedRange[p], -1+mappedRange[p + 1], false);
148 containsHidden = !viscont.hasNext();
151 for (int[] interval = viscont.next(); viscont
152 .hasNext(); rowsel |= columnSelection
153 .intersects(interval[p], interval[p + 1]))
159 rowsel = columnSelection.intersects(-1+mappedRange[p],
160 -1+mappedRange[p + 1]);
168 * Return mapped cell intersecting pStart \
170 * FIXME: REDUNDANT METHOD - COULD DELETE FIXME: OR RE-IMPLEMENT AS EFFICIENT
176 * @return nearest full cell containing pStart - does not set
177 * contactInterval.pEnd or cEnd to equivalent position on pEnd !
179 public contactInterval mapFor(int pStart, int pEnd)
189 if (pEnd >= graphHeight)
191 pEnd = graphHeight - 1;
193 if (pStart >= graphHeight)
195 pStart = graphHeight - pixels_step;
197 int step = Math.floorDiv(pStart, pixels_step);
198 return findStep(step);
204 * [0..) n steps covering height and contactHeight
205 * @return contactInterval for step, or null if out of bounds
207 contactInterval findStep(int step)
209 if (step < 0 || step > lastStep)
213 return new contactInterval((int) Math.floor(contacts_step * step),
214 -1 + (int) Math.min(contact_height,
215 Math.floor(contacts_step * (step + 1))),
217 Math.min(graphHeight, (step + 1) * pixels_step) - 1);
221 * return the cell containing given pixel
224 * @return range for pCEntre
226 public contactInterval mapFor(int pCentre)
228 if (pCentre >= graphHeight + pixels_step)
232 int step = Math.floorDiv(pCentre, pixels_step);
233 return findStep(step);
236 public List<contactInterval> allSteps()
238 contactInterval[] array = new contactInterval[lastStep + 1];
239 int csum = 0, psum = 0;
240 for (int i = 0; i <= lastStep; i++)
242 array[i] = findStep(i);
243 csum += 1 + array[i].cEnd - array[i].cStart;
244 psum += 1 + array[i].pEnd - array[i].pStart;
246 if (csum != contact_height || psum != graphHeight)
248 System.err.println("csum = " + csum + " not " + contact_height + "\n"
249 + "psum = " + psum + " not " + graphHeight);
252 return Arrays.asList(array);
255 public Iterator<contactInterval> iterateOverContactIntervals(
258 // NOT YET IMPLEMENTED
260 // int cstart = 0, cend;
263 // eht = y2 - graphHeight; ht >= eht; ht -= pixels_step)
265 // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
266 // cend = (int) Math.min(contact_height,
267 // Math.ceil(cstart + contacts_per_pixel * pixels_step));
269 // return new Iterator<contactIntervals>() {
272 // public boolean hasNext()
274 // // TODO Auto-generated method stub
279 // public contactIntervals next()
281 // // TODO Auto-generated method stub