Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
ContactGeometry | 39 | 51 | 27 | ||
ContactGeometry.contactInterval | 103 | 9 | 5 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package jalview.renderer; | |
22 | ||
23 | import java.util.Arrays; | |
24 | import java.util.Iterator; | |
25 | import java.util.List; | |
26 | ||
27 | import jalview.datamodel.ColumnSelection; | |
28 | import jalview.datamodel.ContactListI; | |
29 | import jalview.datamodel.HiddenColumns; | |
30 | import jalview.renderer.ContactGeometry.contactInterval; | |
31 | ||
32 | /** | |
33 | * encapsulate logic for mapping between positions in a ContactList and their | |
34 | * rendered representation in a given number of pixels. | |
35 | * | |
36 | * @author jprocter | |
37 | * | |
38 | */ | |
39 | public class ContactGeometry | |
40 | { | |
41 | ||
42 | final ContactListI contacts; | |
43 | ||
44 | /** | |
45 | * how many pixels per contact (1..many) | |
46 | */ | |
47 | final int pixels_step; | |
48 | ||
49 | /** | |
50 | * how many contacts per pixel (many > 0) | |
51 | */ | |
52 | final double contacts_per_pixel; | |
53 | ||
54 | /** | |
55 | * number of contacts being mapped | |
56 | */ | |
57 | final int contact_height; | |
58 | ||
59 | /** | |
60 | * number of pixels to map contact_height to | |
61 | */ | |
62 | final int graphHeight; | |
63 | ||
64 | /** | |
65 | * number of contacts for each pixel_step - to last whole contact | |
66 | */ | |
67 | final double contacts_step; | |
68 | ||
69 | final int lastStep; | |
70 | ||
71 | /** | |
72 | * Bean used to map from a range of contacts to a range of pixels | |
73 | * | |
74 | * @param contacts | |
75 | * @param graphHeight | |
76 | * Number of pixels to map given range of contacts | |
77 | */ | |
78 | 144932 | public ContactGeometry(final ContactListI contacts, int graphHeight) |
79 | { | |
80 | 144932 | this.contacts = contacts; |
81 | 144932 | this.graphHeight = graphHeight; |
82 | 144932 | contact_height = contacts.getContactHeight(); |
83 | // fractional number of contacts covering each pixel | |
84 | 144932 | contacts_per_pixel = (graphHeight <= 1) ? contact_height |
85 | : ((double) contact_height) / ((double) graphHeight); | |
86 | ||
87 | 144932 | if (contacts_per_pixel >= 1) |
88 | { | |
89 | // many contacts rendered per pixel | |
90 | 31897 | pixels_step = 1; |
91 | } | |
92 | else | |
93 | { | |
94 | // pixel height for each contact | |
95 | 113035 | pixels_step = (int) Math |
96 | .ceil(((double) graphHeight) / (double) contact_height); | |
97 | } | |
98 | 144932 | contacts_step = pixels_step * contacts_per_pixel; |
99 | 144932 | lastStep = (int) Math.min((double) graphHeight, |
100 | ((double) graphHeight) / ((double) pixels_step)); | |
101 | } | |
102 | ||
103 | public class contactInterval | |
104 | { | |
105 | 147124289 | public contactInterval(int cStart, int cEnd, int pStart, int pEnd) |
106 | { | |
107 | 147124288 | this.cStart = cStart; |
108 | 147124287 | this.cEnd = cEnd; |
109 | 147124286 | this.pStart = pStart; |
110 | 147124285 | this.pEnd = pEnd; |
111 | } | |
112 | ||
113 | // range on contact list | |
114 | public final int cStart; | |
115 | ||
116 | public final int cEnd; | |
117 | ||
118 | // range in pixels | |
119 | public final int pStart; | |
120 | ||
121 | public final int pEnd; | |
122 | ||
123 | 174 | @Override |
124 | public boolean equals(Object obj) | |
125 | { | |
126 | 174 | if (obj == null || !(obj instanceof contactInterval)) |
127 | { | |
128 | 0 | return false; |
129 | } | |
130 | 174 | contactInterval them = (contactInterval) obj; |
131 | 174 | return cStart == them.cStart && cEnd == them.cEnd && pEnd == them.pEnd |
132 | && pStart == them.pStart; | |
133 | } | |
134 | ||
135 | 4 | @Override |
136 | public String toString() | |
137 | { | |
138 | 4 | return "Contacts [" + cStart + "," + cEnd + "] : Pixels [" + pStart |
139 | + "," + pEnd + "]"; | |
140 | } | |
141 | } | |
142 | ||
143 | /** | |
144 | * | |
145 | * @param columnSelection | |
146 | * @param ci | |
147 | * @param visibleOnly | |
148 | * - when true, only test intersection of visible columns given | |
149 | * matrix range | |
150 | * @return true if the range on the matrix specified by ci intersects with | |
151 | * selected columns in the ContactListI's reference frame. | |
152 | */ | |
153 | ||
154 | 142091 | boolean intersects(contactInterval ci, ColumnSelection columnSelection, |
155 | HiddenColumns hiddenColumns, boolean visibleOnly) | |
156 | { | |
157 | 142091 | boolean rowsel = false; |
158 | 142091 | final int[] mappedRange = contacts.getMappedPositionsFor(ci.cStart, |
159 | ci.cEnd); | |
160 | 142092 | if (mappedRange == null) |
161 | { | |
162 | 0 | return false; |
163 | } | |
164 | 284184 | for (int p = 0; p < mappedRange.length && !rowsel; p += 2) |
165 | { | |
166 | 142091 | boolean containsHidden = false; |
167 | 142091 | if (visibleOnly && hiddenColumns != null |
168 | && hiddenColumns.hasHiddenColumns()) | |
169 | { | |
170 | // TODO: turn into function on hiddenColumns and create test !! | |
171 | 0 | Iterator<int[]> viscont = hiddenColumns.getVisContigsIterator( |
172 | -1 + mappedRange[p], -1 + mappedRange[p + 1], false); | |
173 | 0 | containsHidden = !viscont.hasNext(); |
174 | 0 | if (!containsHidden) |
175 | { | |
176 | 0 | for (int[] interval = viscont.next(); viscont |
177 | .hasNext(); rowsel |= columnSelection | |
178 | .intersects(interval[p], interval[p + 1])) | |
179 | 0 | ; |
180 | } | |
181 | } | |
182 | else | |
183 | { | |
184 | 142091 | rowsel = columnSelection.intersects(-1 + mappedRange[p], |
185 | -1 + mappedRange[p + 1]); | |
186 | } | |
187 | } | |
188 | 142091 | return rowsel; |
189 | ||
190 | } | |
191 | ||
192 | /** | |
193 | * Return mapped cell intersecting pStart \ | |
194 | * | |
195 | * FIXME: REDUNDANT METHOD - COULD DELETE FIXME: OR RE-IMPLEMENT AS EFFICIENT | |
196 | * RANGE QUERY | |
197 | * | |
198 | * @param pStart | |
199 | * [0..) | |
200 | * @param pEnd | |
201 | * @return nearest full cell containing pStart - does not set | |
202 | * contactInterval.pEnd or cEnd to equivalent position on pEnd ! | |
203 | */ | |
204 | 84 | public contactInterval mapFor(int pStart, int pEnd) |
205 | { | |
206 | 84 | if (pStart < 0) |
207 | { | |
208 | 0 | pStart = 0; |
209 | } | |
210 | 84 | if (pEnd < pStart) |
211 | { | |
212 | 0 | pEnd = pStart; |
213 | } | |
214 | 84 | if (pEnd >= graphHeight) |
215 | { | |
216 | 4 | pEnd = graphHeight - 1; |
217 | } | |
218 | 84 | if (pStart >= graphHeight) |
219 | { | |
220 | 0 | pStart = graphHeight - pixels_step; |
221 | } | |
222 | 84 | int step = Math.floorDiv(pStart, pixels_step); |
223 | 84 | return findStep(step); |
224 | } | |
225 | ||
226 | /** | |
227 | * | |
228 | * @param step | |
229 | * [0..) n steps covering height and contactHeight | |
230 | * @return contactInterval for step, or null if out of bounds | |
231 | */ | |
232 | 147124288 | contactInterval findStep(int step) |
233 | { | |
234 | 147124287 | if (step < 0 || step > lastStep) |
235 | { | |
236 | 0 | return null; |
237 | } | |
238 | 147124288 | return new contactInterval((int) Math.floor(contacts_step * step), |
239 | -1 + (int) Math.min(contact_height, | |
240 | Math.floor(contacts_step * (step + 1))), | |
241 | pixels_step * step, | |
242 | Math.min(graphHeight, (step + 1) * pixels_step) - 1); | |
243 | } | |
244 | ||
245 | /** | |
246 | * return the cell containing given pixel | |
247 | * | |
248 | * @param pCentre | |
249 | * @return range for pCEntre | |
250 | */ | |
251 | 142174 | public contactInterval mapFor(int pCentre) |
252 | { | |
253 | 142173 | if (pCentre >= graphHeight + pixels_step) |
254 | { | |
255 | 0 | return null; |
256 | } | |
257 | 142172 | int step = Math.floorDiv(pCentre, pixels_step); |
258 | 142170 | return findStep(step); |
259 | } | |
260 | ||
261 | 141374 | public List<contactInterval> allSteps() |
262 | { | |
263 | 141374 | contactInterval[] array = new contactInterval[lastStep + 1]; |
264 | 141374 | int csum = 0, psum = 0; |
265 | 147123408 | for (int i = 0; i <= lastStep; i++) |
266 | { | |
267 | 146982034 | array[i] = findStep(i); |
268 | 146982034 | csum += 1 + array[i].cEnd - array[i].cStart; |
269 | 146982034 | psum += 1 + array[i].pEnd - array[i].pStart; |
270 | } | |
271 | 141374 | if (csum != contact_height || psum != graphHeight) |
272 | { | |
273 | 0 | System.err.println("csum = " + csum + " not " + contact_height + "\n" |
274 | + "psum = " + psum + " not " + graphHeight); | |
275 | 0 | return null; |
276 | } | |
277 | 141374 | return Arrays.asList(array); |
278 | } | |
279 | ||
280 | 0 | public Iterator<contactInterval> iterateOverContactIntervals( |
281 | int graphHeight) | |
282 | { | |
283 | // NOT YET IMPLEMENTED | |
284 | 0 | return null; |
285 | // int cstart = 0, cend; | |
286 | // | |
287 | // for (int ht = y2, | |
288 | // eht = y2 - graphHeight; ht >= eht; ht -= pixels_step) | |
289 | // { | |
290 | // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel); | |
291 | // cend = (int) Math.min(contact_height, | |
292 | // Math.ceil(cstart + contacts_per_pixel * pixels_step)); | |
293 | // | |
294 | // return new Iterator<contactIntervals>() { | |
295 | // | |
296 | // @Override | |
297 | // public boolean hasNext() | |
298 | // { | |
299 | // // TODO Auto-generated method stub | |
300 | // return false; | |
301 | // } | |
302 | // | |
303 | // @Override | |
304 | // public contactIntervals next() | |
305 | // { | |
306 | // // TODO Auto-generated method stub | |
307 | // return null; | |
308 | // } | |
309 | // | |
310 | // } | |
311 | } | |
312 | } |