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