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