Merge branch 'documentation/JAL-2325_release2101' into releases/Release_2_10_Branch
[jalview.git] / src / jalview / schemes / ResidueColourScheme.java
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.schemes;
22
23 import jalview.analysis.Conservation;
24 import jalview.datamodel.AnnotatedCollectionI;
25 import jalview.datamodel.ProfileI;
26 import jalview.datamodel.ProfilesI;
27 import jalview.datamodel.SequenceCollectionI;
28 import jalview.datamodel.SequenceI;
29 import jalview.util.ColorUtils;
30 import jalview.util.Comparison;
31 import jalview.util.MessageManager;
32
33 import java.awt.Color;
34 import java.util.Map;
35
36 /**
37  * DOCUMENT ME!
38  * 
39  * @author $author$
40  * @version $Revision$
41  */
42 public class ResidueColourScheme implements ColourSchemeI
43 {
44   final int[] symbolIndex;
45
46   boolean conservationColouring = false;
47
48   Color[] colors = null;
49
50   int threshold = 0;
51
52   /* Set when threshold colouring to either pid_gaps or pid_nogaps */
53   protected boolean ignoreGaps = false;
54
55   /*
56    * Consensus data indexed by column
57    */
58   ProfilesI consensus;
59
60   /*
61    * Conservation string as a char array 
62    */
63   char[] conservation;
64
65   /*
66    * The conservation slider percentage setting 
67    */
68   int inc = 30;
69
70   /**
71    * Creates a new ResidueColourScheme object.
72    * 
73    * @param final int[] index table into colors (ResidueProperties.naIndex or
74    *        ResidueProperties.aaIndex)
75    * @param colors
76    *          colours for symbols in sequences
77    * @param threshold
78    *          threshold for conservation shading
79    */
80   public ResidueColourScheme(int[] aaOrnaIndex, Color[] colours,
81           int threshold)
82   {
83     symbolIndex = aaOrnaIndex;
84     this.colors = colours;
85     this.threshold = threshold;
86   }
87
88   /**
89    * Creates a new ResidueColourScheme object with a lookup table for indexing
90    * the colour map
91    */
92   public ResidueColourScheme(int[] aaOrNaIndex)
93   {
94     symbolIndex = aaOrNaIndex;
95   }
96
97   /**
98    * Creates a new ResidueColourScheme object - default constructor for
99    * non-sequence dependent colourschemes
100    */
101   public ResidueColourScheme()
102   {
103     symbolIndex = null;
104   }
105
106   /**
107    * Find a colour without an index in a sequence
108    */
109   @Override
110   public Color findColour(char c)
111   {
112     return colors == null ? Color.white : colors[symbolIndex[c]];
113   }
114
115   @Override
116   public Color findColour(char c, int j, SequenceI seq)
117   {
118     Color currentColour;
119
120     if (colors != null && symbolIndex != null && (threshold == 0)
121             || aboveThreshold(c, j))
122     {
123       currentColour = colors[symbolIndex[c]];
124     }
125     else
126     {
127       currentColour = Color.white;
128     }
129
130     if (conservationColouring)
131     {
132       currentColour = applyConservation(currentColour, j);
133     }
134
135     return currentColour;
136   }
137
138   /**
139    * Get the percentage threshold for this colour scheme
140    * 
141    * @return Returns the percentage threshold
142    */
143   @Override
144   public int getThreshold()
145   {
146     return threshold;
147   }
148
149   /**
150    * Sets the percentage consensus threshold value, and whether gaps are ignored
151    * in percentage identity calculation
152    * 
153    * @param consensusThreshold
154    * @param ignoreGaps
155    */
156   @Override
157   public void setThreshold(int consensusThreshold, boolean ignoreGaps)
158   {
159     threshold = consensusThreshold;
160     this.ignoreGaps = ignoreGaps;
161   }
162
163   /**
164    * Answers true if there is a consensus profile for the specified column, and
165    * the given residue matches the consensus (or joint consensus) residue for
166    * the column, and the percentage identity for the profile is equal to or
167    * greater than the current threshold; else answers false. The percentage
168    * calculation depends on whether or not we are ignoring gapped sequences.
169    * 
170    * @param residue
171    * @param column
172    *          (index into consensus profiles)
173    * 
174    * @return
175    * @see #setThreshold(int, boolean)
176    */
177   public boolean aboveThreshold(char residue, int column)
178   {
179     if ('a' <= residue && residue <= 'z')
180     {
181       // TO UPPERCASE !!!
182       // Faster than toUpperCase
183       residue -= ('a' - 'A');
184     }
185
186     if (consensus == null)
187     {
188       return false;
189     }
190
191     ProfileI profile = consensus.get(column);
192
193     /*
194      * test whether this is the consensus (or joint consensus) residue
195      */
196     if (profile != null
197             && profile.getModalResidue().contains(String.valueOf(residue)))
198     {
199       if (profile.getPercentageIdentity(ignoreGaps) >= threshold)
200       {
201         return true;
202       }
203     }
204
205     return false;
206   }
207
208   @Override
209   public boolean conservationApplied()
210   {
211     return conservationColouring;
212   }
213
214   @Override
215   public void setConservationApplied(boolean conservationApplied)
216   {
217     conservationColouring = conservationApplied;
218   }
219
220   @Override
221   public void setConservationInc(int i)
222   {
223     inc = i;
224   }
225
226   @Override
227   public int getConservationInc()
228   {
229     return inc;
230   }
231
232   /**
233    * DOCUMENT ME!
234    * 
235    * @param consensus
236    *          DOCUMENT ME!
237    */
238   @Override
239   public void setConsensus(ProfilesI consensus)
240   {
241     if (consensus == null)
242     {
243       return;
244     }
245
246     this.consensus = consensus;
247   }
248
249   @Override
250   public void setConservation(Conservation cons)
251   {
252     if (cons == null)
253     {
254       conservationColouring = false;
255       conservation = null;
256     }
257     else
258     {
259       conservationColouring = true;
260       int iSize = cons.getConsSequence().getLength();
261       conservation = new char[iSize];
262       for (int i = 0; i < iSize; i++)
263       {
264         conservation[i] = cons.getConsSequence().getCharAt(i);
265       }
266     }
267
268   }
269
270   /**
271    * Applies a combination of column conservation score, and conservation
272    * percentage slider, to 'bleach' out the residue colours towards white.
273    * <p>
274    * If a column is fully conserved (identical residues, conservation score 11,
275    * shown as *), or all 10 physico-chemical properties are conserved
276    * (conservation score 10, shown as +), then the colour is left unchanged.
277    * <p>
278    * Otherwise a 'bleaching' factor is computed and applied to the colour. This
279    * is designed to fade colours for scores of 0-9 completely to white at slider
280    * positions ranging from 18% - 100% respectively.
281    * 
282    * @param currentColour
283    * @param column
284    * 
285    * @return bleached (or unmodified) colour
286    */
287   Color applyConservation(Color currentColour, int column)
288   {
289     if (conservation == null || conservation.length <= column)
290     {
291       return currentColour;
292     }
293     char conservationScore = conservation[column];
294
295     /*
296      * if residues are fully conserved (* or 11), or all properties
297      * are conserved (+ or 10), leave colour unchanged
298      */
299     if (conservationScore == '*' || conservationScore == '+'
300             || conservationScore == (char) 10
301             || conservationScore == (char) 11)
302     {
303       return currentColour;
304     }
305
306     if (Comparison.isGap(conservationScore))
307     {
308       return Color.white;
309     }
310
311     /*
312      * convert score 0-9 to a bleaching factor 1.1 - 0.2
313      */
314     float bleachFactor = (11 - (conservationScore - '0')) / 10f;
315
316     /*
317      * scale this up by 0-5 (percentage slider / 20)
318      * as a result, scores of:         0  1  2  3  4  5  6  7  8  9
319      * fade to white at slider value: 18 20 22 25 29 33 40 50 67 100%
320      */
321     bleachFactor *= (inc / 20f);
322
323     return ColorUtils.bleachColour(currentColour, bleachFactor);
324   }
325
326   @Override
327   public void alignmentChanged(AnnotatedCollectionI alignment,
328           Map<SequenceI, SequenceCollectionI> hiddenReps)
329   {
330   }
331
332   @Override
333   public ColourSchemeI applyTo(AnnotatedCollectionI sg,
334           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
335   {
336     try
337     {
338       return getClass().newInstance();
339     } catch (Exception q)
340     {
341       throw new Error(MessageManager.formatMessage(
342               "error.implementation_error_cannot_duplicate_colour_scheme",
343               new String[] { getClass().getName() }), q);
344     }
345   }
346 }