JAL-629 Tidy up tests and replaced methods before merge to develop
[jalview.git] / src / jalview / renderer / ContactMapRenderer.java
1 /**
2  * 
3  */
4 package jalview.renderer;
5
6 import java.awt.Color;
7 import java.awt.Graphics;
8 import java.util.Iterator;
9
10 import jalview.api.AlignViewportI;
11 import jalview.datamodel.AlignmentAnnotation;
12 import jalview.datamodel.Annotation;
13 import jalview.datamodel.ColumnSelection;
14 import jalview.datamodel.ContactListI;
15 import jalview.datamodel.ContactMatrixI;
16 import jalview.datamodel.ContactRange;
17 import jalview.datamodel.HiddenColumns;
18 import jalview.renderer.api.AnnotationRowRendererI;
19
20 /**
21  * @author jprocter
22  *
23  */
24 public abstract class ContactMapRenderer implements AnnotationRowRendererI
25 {
26   /**
27    * bean holding colours for shading
28    * @author jprocter
29    *
30    */
31   public class Shading
32   {
33     /**
34      * shown when no data available from map
35      */
36     Color no_data;
37     /**
38      * shown for region not currently visible - should normally not see this
39      */
40     Color hidden;
41     /**
42      * linear shading scheme min/max
43      */
44     Color maxColor, minColor;
45
46     /**
47      * linear shading scheme min/max for selected region
48      */
49     Color selMinColor, selMaxColor;
50
51     public Shading(Color no_data, Color hidden, Color maxColor,
52             Color minColor, Color selMinColor, Color selMaxColor)
53     {
54       super();
55       this.no_data = no_data;
56       this.hidden = hidden;
57       this.maxColor = maxColor;
58       this.minColor = minColor;
59       this.selMinColor = selMinColor;
60       this.selMaxColor = selMaxColor;
61     }
62
63   }
64
65   final Shading shade;
66
67   /**
68    * build an EBI-AlphaFold style renderer of PAE matrices
69    * @return
70    */
71   public static ContactMapRenderer newPAERenderer()
72   {
73     return new ContactMapRenderer()
74     {
75       @Override
76       public Shading getShade()
77       {
78         return new Shading(Color.pink, Color.red,
79
80                 new Color(246, 252, 243), new Color(0, 60, 26),
81                 new Color(26, 0, 60), new Color(243, 246, 252));
82       }
83     };
84   }
85
86   /**
87    * 
88    * @return instance of Shading used to initialise the renderer
89    */
90   public abstract Shading getShade();
91
92   public ContactMapRenderer()
93   {
94     this.shade = getShade();
95   }
96
97   @Override
98   public void renderRow(Graphics g, int charWidth, int charHeight,
99           boolean hasHiddenColumns, AlignViewportI viewport,
100           HiddenColumns hiddenColumns, ColumnSelection columnSelection,
101           AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes,
102           int eRes, float min, float max, int y)
103   {
104     if (sRes > aa_annotations.length)
105     {
106       return;
107     }
108     eRes = Math.min(eRes, aa_annotations.length);
109
110     int x = 0, y2 = y;
111
112     g.setColor(shade.no_data);
113
114     g.drawLine(x, y2, (eRes - sRes) * charWidth, y2);
115
116     int column;
117     int aaMax = aa_annotations.length - 1;
118     ContactMatrixI cm = viewport.getContactMatrix(_aa);
119     while (x < eRes - sRes)
120     {
121       column = sRes + x;
122       if (hasHiddenColumns)
123       {
124         column = hiddenColumns.visibleToAbsoluteColumn(column);
125       }
126       // TODO: highlight columns selected
127       boolean colsel = false;
128       if (columnSelection != null)
129       {
130         colsel = columnSelection.contains(column);
131       }
132
133       if (column > aaMax)
134       {
135         break;
136       }
137
138       if (aa_annotations[column] == null)
139       {
140         x++;
141         continue;
142       }
143       ContactListI contacts = viewport.getContactList(_aa, column);
144       if (contacts == null)
145       {
146         x++;
147         continue;
148       }
149       Color gpcol = (cm==null) ? Color.white: cm.getColourForGroup(cm.getGroupsFor(column));
150       // feature still in development - highlight or omit regions hidden in
151       // the alignment - currently marks them as red rows
152       boolean maskHiddenCols = false;
153       // TODO: pass visible column mask to the ContactGeometry object so it maps
154       // only visible contacts to geometry
155       // Bean holding mapping from contact list to pixels
156       final ContactGeometry cgeom = new ContactGeometry(contacts,
157               _aa.graphHeight);
158
159       for (int ht = y2, eht = y2
160               - _aa.graphHeight; ht >= eht; ht -= cgeom.pixels_step)
161       {
162         ContactGeometry.contactInterval ci = cgeom.mapFor(y2 - ht,
163                 y2 - ht + cgeom.pixels_step);
164         // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
165         // cend = (int) Math.min(contact_height,
166         // Math.ceil(cstart + contacts_per_pixel * pixels_step));
167
168         Color col;
169         boolean rowsel = false, containsHidden = false;
170         if (columnSelection != null)
171         {
172           if (_aa.sequenceRef == null)
173           {
174             rowsel = columnSelection.intersects(ci.cStart, ci.cEnd);
175           }
176           else
177           {
178             // TODO check we have correctly mapped cstart to local sequence
179             // numbering
180             int s = _aa.sequenceRef.findIndex(ci.cStart);
181             int e = _aa.sequenceRef.findIndex(ci.cEnd);
182             if (maskHiddenCols && hasHiddenColumns)
183             {
184               // TODO: turn into function and create test !!
185               Iterator<int[]> viscont = hiddenColumns
186                       .getVisContigsIterator(s, e, false);
187               containsHidden = !viscont.hasNext();
188             }
189             if (s > 0 && s < _aa.sequenceRef.getLength())
190             {
191               rowsel = columnSelection.intersects(s, e);
192             }
193
194           }
195         }
196         // TODO: show selected region
197         if (colsel || rowsel)
198         {
199
200           col = getSelectedColorForRange(min, max, contacts, ci.cStart,
201                   ci.cEnd);
202           if (colsel && rowsel)
203           {
204             col = new Color(col.getBlue(), col.getGreen(), col.getRed());
205           }
206           else
207           {
208             col = new Color(col.getBlue(), col.getBlue(), col.getBlue());
209           }
210         }
211         else
212         {
213           col = getColorForRange(min, max, contacts, ci.cStart, ci.cEnd);
214         }
215         if (containsHidden)
216         {
217           col = shade.hidden;
218         }
219         if (gpcol!=null && gpcol!=Color.white) {
220           // todo - could overlay group as a transparent rectangle ?
221           col = new Color((int)(((float)(col.getRed()+gpcol.getRed()))/2f),
222                   (int)(((float)(col.getGreen()+gpcol.getGreen()))/2f),
223                   (int)(((float)(col.getBlue()+gpcol.getBlue()))/2f));
224         }
225         g.setColor(col);
226         
227         if (cgeom.pixels_step > 1)
228         {
229           g.fillRect(x * charWidth, ht, charWidth, 1 + cgeom.pixels_step);
230         }
231         else
232         {
233           g.drawLine(x * charWidth, ht, (x + 1) * charWidth, ht);
234         }
235       }
236       x++;
237     }
238
239   }
240
241   Color shadeFor(float min, float max, float value)
242   {
243     return jalview.util.ColorUtils.getGraduatedColour(value, 0, shade.minColor,
244             max, shade.maxColor);
245   }
246
247   public Color getColorForRange(float min, float max, ContactListI cl,
248           int i, int j)
249   {
250     ContactRange cr = cl.getRangeFor(i, j);
251     // average for moment - probably more interested in maxIntProj though
252     return shadeFor(min, max, (float) cr.getMean());
253   }
254
255   public Color getSelectedColorForRange(float min, float max,
256           ContactListI cl, int i, int j)
257   {
258     ContactRange cr = cl.getRangeFor(i, j);
259     // average for moment - probably more interested in maxIntProj though
260     return jalview.util.ColorUtils.getGraduatedColour((float) cr.getMean(),
261             0, shade.selMinColor, max, shade.selMaxColor);
262   }
263
264 }