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