9fd4de6cd02c2502ca94cfb106c4ab2c2d5059d4
[jalview.git] / src / jalview / renderer / ContactGeometry.java
1 package jalview.renderer;
2
3 import java.util.Iterator;
4
5 import jalview.datamodel.ColumnSelection;
6 import jalview.datamodel.ContactListI;
7 import jalview.datamodel.HiddenColumns;
8 import jalview.renderer.ContactGeometry.contactInterval;
9
10 /**
11  * encapsulate logic for mapping between positions in a ContactList and their
12  * rendered representation in a given number of pixels.
13  * 
14  * @author jprocter
15  *
16  */
17 public class ContactGeometry
18 {
19
20   final ContactListI contacts;
21
22   final int pixels_step;
23
24   final double contacts_per_pixel;
25
26   final int contact_height;
27
28   final int graphHeight;
29
30   public ContactGeometry(final ContactListI contacts, int graphHeight)
31   {
32     this.contacts = contacts;
33     this.graphHeight = graphHeight;
34     contact_height = contacts.getContactHeight();
35     // fractional number of contacts covering each pixel
36     contacts_per_pixel = (graphHeight < 1) ? contact_height
37             : ((double) contact_height) / ((double) graphHeight);
38
39     if (contacts_per_pixel >= 1)
40     {
41       // many contacts rendered per pixel
42       pixels_step = 1;
43     }
44     else
45     {
46       // pixel height for each contact
47       pixels_step = (int) Math
48               .ceil(((double) graphHeight) / (double) contact_height);
49     }
50   }
51
52   public class contactInterval
53   {
54     public contactInterval(int cStart, int cEnd, int pStart, int pEnd)
55     {
56       this.cStart = cStart;
57       this.cEnd = cEnd;
58       this.pStart = pStart;
59       this.pEnd = pEnd;
60     }
61
62     // range on contact list
63     public final int cStart;
64
65     public final int cEnd;
66
67     // range in pixels
68     public final int pStart;
69
70     public final int pEnd;
71
72   }
73
74   /**
75    * 
76    * @param columnSelection
77    * @param ci
78    * @param visibleOnly
79    *          - when true, only test intersection of visible columns given
80    *          matrix range
81    * @return true if the range on the matrix specified by ci intersects with
82    *         selected columns in the ContactListI's reference frame.
83    */
84
85   boolean intersects(contactInterval ci, ColumnSelection columnSelection,
86           HiddenColumns hiddenColumns, boolean visibleOnly)
87   {
88     boolean rowsel = false;
89     final int[] mappedRange = contacts.getMappedPositionsFor(ci.cStart,
90             ci.cEnd);
91     if (mappedRange == null)
92     {
93       return false;
94     }
95     for (int p = 0; p < mappedRange.length && !rowsel; p += 2)
96     {
97       boolean containsHidden = false;
98       if (visibleOnly && hiddenColumns != null
99               && hiddenColumns.hasHiddenColumns())
100       {
101         // TODO: turn into function on hiddenColumns and create test !!
102         Iterator<int[]> viscont = hiddenColumns.getVisContigsIterator(
103                 mappedRange[p], mappedRange[p + 1], false);
104         containsHidden = !viscont.hasNext();
105         if (!containsHidden)
106         {
107           for (int[] interval = viscont.next(); viscont
108                   .hasNext(); rowsel |= columnSelection
109                           .intersects(interval[p], interval[p + 1]))
110             ;
111         }
112       }
113       else
114       {
115         rowsel = columnSelection.intersects(mappedRange[p],
116                 mappedRange[p + 1]);
117       }
118     }
119     return rowsel;
120
121   }
122
123   /**
124    * 
125    * @param pStart
126    * @param pEnd
127    * @return range for
128    */
129   public contactInterval mapFor(int pStart, int pEnd)
130   {
131     int cStart = (int) Math.floor(pStart * contacts_per_pixel);
132     contactInterval ci = new contactInterval(cStart,
133             (int) Math.min(contact_height,
134                     Math.ceil(
135                             cStart + (pEnd - pStart) * contacts_per_pixel)),
136             pStart, pEnd);
137
138     return ci;
139   }
140
141   /**
142    * return the cell containing given pixel
143    * 
144    * @param pCentre
145    * @return range for pCEntre
146    */
147   public contactInterval mapFor(int pCentre)
148   {
149     int pStart = Math.max(pCentre - pixels_step, 0);
150     int pEnd = Math.min(pStart + pixels_step, graphHeight);
151     int cStart = (int) Math.floor(pStart * contacts_per_pixel);
152     contactInterval ci = new contactInterval(cStart,
153             (int) Math.min(contact_height,
154                     Math.ceil(cStart + (pixels_step) * contacts_per_pixel)),
155             pStart, pEnd);
156
157     return ci;
158   }
159
160   public Iterator<contactInterval> iterateOverContactIntervals(
161           int graphHeight)
162   {
163     // NOT YET IMPLEMENTED
164     return null;
165     // int cstart = 0, cend;
166     //
167     // for (int ht = y2,
168     // eht = y2 - graphHeight; ht >= eht; ht -= pixels_step)
169     // {
170     // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
171     // cend = (int) Math.min(contact_height,
172     // Math.ceil(cstart + contacts_per_pixel * pixels_step));
173     //
174     // return new Iterator<contactIntervals>() {
175     //
176     // @Override
177     // public boolean hasNext()
178     // {
179     // // TODO Auto-generated method stub
180     // return false;
181     // }
182     //
183     // @Override
184     // public contactIntervals next()
185     // {
186     // // TODO Auto-generated method stub
187     // return null;
188     // }
189     //
190     // }
191   }
192 }