JAL-2349 allow PAE or other contact matrices to hold a coordinate mapping allowing...
[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    * @param columnSelection
76    * @param ci
77    * @param visibleOnly - when true, only test intersection of visible columns given matrix range 
78    * @return true if the range on the matrix specified by ci intersects with selected columns in the ContactListI's reference frame.
79    */
80   
81   boolean intersects(contactInterval ci,ColumnSelection columnSelection, HiddenColumns hiddenColumns,  boolean visibleOnly) {
82     boolean rowsel = false;
83     final int[] mappedRange = contacts.getMappedPositionsFor(ci.cStart, ci.cEnd);
84     if (mappedRange==null)
85     {
86       return false;
87     }
88     boolean containsHidden=false;
89     if (visibleOnly && hiddenColumns!=null && hiddenColumns.hasHiddenColumns())
90     {
91       // TODO: turn into function on hiddenColumns and create test !!
92       Iterator<int[]> viscont = hiddenColumns
93               .getVisContigsIterator(mappedRange[0], mappedRange[1], false);
94       containsHidden = !viscont.hasNext();
95       if (!containsHidden)
96       {
97         for (int[] interval=viscont.next();viscont.hasNext();
98         rowsel |= columnSelection.intersects(interval[0],interval[1]))
99           ;
100       }
101     }
102     else
103     {
104       // if containsHidden is true mappedRange is not visible
105       if (containsHidden)
106       {
107         rowsel = columnSelection.intersects(mappedRange[0], mappedRange[1]);
108       }
109     }
110     return rowsel;
111
112   }
113
114   /**
115    * 
116    * @param pStart
117    * @param pEnd
118    * @return range for
119    */
120   public contactInterval mapFor(int pStart, int pEnd)
121   {
122     int cStart = (int) Math.floor(pStart * contacts_per_pixel);
123     contactInterval ci = new contactInterval(cStart,
124             (int) Math.min(contact_height,
125                     Math.ceil(
126                             cStart + (pEnd - pStart) * contacts_per_pixel)),
127             pStart, pEnd);
128
129     return ci;
130   }
131
132   /**
133    * return the cell containing given pixel
134    * 
135    * @param pCentre
136    * @return range for pCEntre
137    */
138   public contactInterval mapFor(int pCentre)
139   {
140     int pStart = Math.max(pCentre - pixels_step, 0);
141     int pEnd = Math.min(pStart + pixels_step, graphHeight);
142     int cStart = (int) Math.floor(pStart * contacts_per_pixel);
143     contactInterval ci = new contactInterval(cStart,
144             (int) Math.min(contact_height,
145                     Math.ceil(cStart + (pixels_step) * contacts_per_pixel)),
146             pStart, pEnd);
147
148     return ci;
149   }
150
151   public Iterator<contactInterval> iterateOverContactIntervals(
152           int graphHeight)
153   {
154     // NOT YET IMPLEMENTED
155     return null;
156     // int cstart = 0, cend;
157     //
158     // for (int ht = y2,
159     // eht = y2 - graphHeight; ht >= eht; ht -= pixels_step)
160     // {
161     // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
162     // cend = (int) Math.min(contact_height,
163     // Math.ceil(cstart + contacts_per_pixel * pixels_step));
164     //
165     // return new Iterator<contactIntervals>() {
166     //
167     // @Override
168     // public boolean hasNext()
169     // {
170     // // TODO Auto-generated method stub
171     // return false;
172     // }
173     //
174     // @Override
175     // public contactIntervals next()
176     // {
177     // // TODO Auto-generated method stub
178     // return null;
179     // }
180     //
181     // }
182   }
183 }