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
56 * Number of pixels to map given range of contacts
58 public ContactGeometry(final ContactListI contacts, int graphHeight)
60 this.contacts = contacts;
61 this.graphHeight = graphHeight;
62 contact_height = contacts.getContactHeight();
63 // fractional number of contacts covering each pixel
64 contacts_per_pixel = (graphHeight <= 1) ? contact_height
65 : ((double) contact_height) / ((double) graphHeight);
67 if (contacts_per_pixel >= 1)
69 // many contacts rendered per pixel
74 // pixel height for each contact
75 pixels_step = (int) Math
76 .ceil(((double) graphHeight) / (double) contact_height);
78 contacts_step = pixels_step * contacts_per_pixel;
79 lastStep = (int) Math.min((double) graphHeight,
80 ((double) graphHeight) / ((double) pixels_step));
83 public class contactInterval
85 public contactInterval(int cStart, int cEnd, int pStart, int pEnd)
93 // range on contact list
94 public final int cStart;
96 public final int cEnd;
99 public final int pStart;
101 public final int pEnd;
104 public boolean equals(Object obj)
106 if (obj == null || !(obj instanceof contactInterval))
110 contactInterval them = (contactInterval) obj;
111 return cStart == them.cStart && cEnd == them.cEnd && pEnd == them.pEnd
112 && pStart == them.pStart;
116 public String toString()
118 return "Contacts [" + cStart + "," + cEnd + "] : Pixels [" + pStart
125 * @param columnSelection
128 * - when true, only test intersection of visible columns given
130 * @return true if the range on the matrix specified by ci intersects with
131 * selected columns in the ContactListI's reference frame.
134 boolean intersects(contactInterval ci, ColumnSelection columnSelection,
135 HiddenColumns hiddenColumns, boolean visibleOnly)
137 boolean rowsel = false;
138 final int[] mappedRange = contacts.getMappedPositionsFor(ci.cStart,
140 if (mappedRange == null)
144 for (int p = 0; p < mappedRange.length && !rowsel; p += 2)
146 boolean containsHidden = false;
147 if (visibleOnly && hiddenColumns != null
148 && hiddenColumns.hasHiddenColumns())
150 // TODO: turn into function on hiddenColumns and create test !!
151 Iterator<int[]> viscont = hiddenColumns.getVisContigsIterator(
152 -1 + mappedRange[p], -1 + mappedRange[p + 1], false);
153 containsHidden = !viscont.hasNext();
156 for (int[] interval = viscont.next(); viscont
157 .hasNext(); rowsel |= columnSelection
158 .intersects(interval[p], interval[p + 1]))
164 rowsel = columnSelection.intersects(-1 + mappedRange[p],
165 -1 + mappedRange[p + 1]);
173 * Return mapped cell intersecting pStart \
175 * FIXME: REDUNDANT METHOD - COULD DELETE FIXME: OR RE-IMPLEMENT AS EFFICIENT
181 * @return nearest full cell containing pStart - does not set
182 * contactInterval.pEnd or cEnd to equivalent position on pEnd !
184 public contactInterval mapFor(int pStart, int pEnd)
194 if (pEnd >= graphHeight)
196 pEnd = graphHeight - 1;
198 if (pStart >= graphHeight)
200 pStart = graphHeight - pixels_step;
202 int step = Math.floorDiv(pStart, pixels_step);
203 return findStep(step);
209 * [0..) n steps covering height and contactHeight
210 * @return contactInterval for step, or null if out of bounds
212 contactInterval findStep(int step)
214 if (step < 0 || step > lastStep)
218 return new contactInterval((int) Math.floor(contacts_step * step),
219 -1 + (int) Math.min(contact_height,
220 Math.floor(contacts_step * (step + 1))),
222 Math.min(graphHeight, (step + 1) * pixels_step) - 1);
226 * return the cell containing given pixel
229 * @return range for pCEntre
231 public contactInterval mapFor(int pCentre)
233 if (pCentre >= graphHeight + pixels_step)
237 int step = Math.floorDiv(pCentre, pixels_step);
238 return findStep(step);
241 public List<contactInterval> allSteps()
243 contactInterval[] array = new contactInterval[lastStep + 1];
244 int csum = 0, psum = 0;
245 for (int i = 0; i <= lastStep; i++)
247 array[i] = findStep(i);
248 csum += 1 + array[i].cEnd - array[i].cStart;
249 psum += 1 + array[i].pEnd - array[i].pStart;
251 if (csum != contact_height || psum != graphHeight)
253 System.err.println("csum = " + csum + " not " + contact_height + "\n"
254 + "psum = " + psum + " not " + graphHeight);
257 return Arrays.asList(array);
260 public Iterator<contactInterval> iterateOverContactIntervals(
263 // NOT YET IMPLEMENTED
265 // int cstart = 0, cend;
268 // eht = y2 - graphHeight; ht >= eht; ht -= pixels_step)
270 // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
271 // cend = (int) Math.min(contact_height,
272 // Math.ceil(cstart + contacts_per_pixel * pixels_step));
274 // return new Iterator<contactIntervals>() {
277 // public boolean hasNext()
279 // // TODO Auto-generated method stub
284 // public contactIntervals next()
286 // // TODO Auto-generated method stub