b0822cadd7614d3369a9d514d93d56d8400ceccf
[jalview.git] / src / jalview / schemes / AnnotationColourGradient.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.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.AnnotatedCollectionI;
26 import jalview.datamodel.Annotation;
27 import jalview.datamodel.GraphLine;
28 import jalview.datamodel.SequenceCollectionI;
29 import jalview.datamodel.SequenceI;
30 import jalview.renderer.AnnotationRenderer;
31 import jalview.util.Comparison;
32
33 import java.awt.Color;
34 import java.util.IdentityHashMap;
35 import java.util.Map;
36
37 public class AnnotationColourGradient extends FollowerColourScheme
38 {
39   public static final int NO_THRESHOLD = -1;
40
41   public static final int BELOW_THRESHOLD = 0;
42
43   public static final int ABOVE_THRESHOLD = 1;
44
45   public AlignmentAnnotation annotation;
46
47   int aboveAnnotationThreshold = -1;
48
49   public boolean thresholdIsMinMax = false;
50
51   GraphLine annotationThreshold;
52
53   float r1, g1, b1, rr, gg, bb;
54
55   private boolean predefinedColours = false;
56
57   private boolean seqAssociated = false;
58   /**
59    * false if the scheme was constructed without a minColour and maxColour used
60    * to decide if existing colours should be taken from annotation elements when
61    * they exist
62    */
63   private boolean noGradient = false;
64   IdentityHashMap<SequenceI, AlignmentAnnotation> seqannot = null;
65
66   @Override
67   public ColourSchemeI applyTo(AnnotatedCollectionI sg,
68           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
69   {
70     AnnotationColourGradient acg = new AnnotationColourGradient(annotation,
71             colourScheme, aboveAnnotationThreshold);
72     acg.thresholdIsMinMax = thresholdIsMinMax;
73     acg.annotationThreshold = (annotationThreshold == null) ? null
74             : new GraphLine(annotationThreshold);
75     acg.r1 = r1;
76     acg.g1 = g1;
77     acg.b1 = b1;
78     acg.rr = rr;
79     acg.gg = gg;
80     acg.bb = bb;
81     acg.predefinedColours = predefinedColours;
82     acg.seqAssociated = seqAssociated;
83     acg.noGradient = noGradient;
84     return acg;
85   }
86
87   /**
88    * Creates a new AnnotationColourGradient object.
89    */
90   public AnnotationColourGradient(AlignmentAnnotation annotation,
91           ColourSchemeI originalColour, int aboveThreshold)
92   {
93     if (originalColour instanceof AnnotationColourGradient)
94     {
95       colourScheme = ((AnnotationColourGradient) originalColour).colourScheme;
96     }
97     else
98     {
99       colourScheme = originalColour;
100     }
101
102     this.annotation = annotation;
103
104     aboveAnnotationThreshold = aboveThreshold;
105
106     if (aboveThreshold != NO_THRESHOLD && annotation.threshold != null)
107     {
108       annotationThreshold = annotation.threshold;
109     }
110     // clear values so we don't get weird black bands...
111     r1 = 254;
112     g1 = 254;
113     b1 = 254;
114     rr = 0;
115     gg = 0;
116     bb = 0;
117
118     noGradient = true;
119   }
120
121   /**
122    * Creates a new AnnotationColourGradient object.
123    */
124   public AnnotationColourGradient(AlignmentAnnotation annotation,
125           Color minColour, Color maxColour, int aboveThreshold)
126   {
127     this.annotation = annotation;
128
129     aboveAnnotationThreshold = aboveThreshold;
130
131     if (aboveThreshold != NO_THRESHOLD && annotation.threshold != null)
132     {
133       annotationThreshold = annotation.threshold;
134     }
135
136     r1 = minColour.getRed();
137     g1 = minColour.getGreen();
138     b1 = minColour.getBlue();
139
140     rr = maxColour.getRed() - r1;
141     gg = maxColour.getGreen() - g1;
142     bb = maxColour.getBlue() - b1;
143
144     noGradient = false;
145     aamax = annotation.graphMax;
146     aamin = annotation.graphMin;
147     if (annotation.isRNA())
148     {
149       ColourSchemeProperty.initRnaHelicesShading(1 + (int) aamax);
150     }
151   }
152
153   @Override
154   public void alignmentChanged(AnnotatedCollectionI alignment,
155           Map<SequenceI, SequenceCollectionI> hiddenReps)
156   {
157     super.alignmentChanged(alignment, hiddenReps);
158
159     if (seqAssociated && annotation.getCalcId() != null)
160     {
161       if (seqannot != null)
162       {
163         seqannot.clear();
164       }
165       else
166       {
167         seqannot = new IdentityHashMap<SequenceI, AlignmentAnnotation>();
168       }
169       // resolve the context containing all the annotation for the sequence
170       AnnotatedCollectionI alcontext = alignment instanceof AlignmentI ? alignment
171               : alignment.getContext();
172       boolean f = true,rna=false;
173       for (AlignmentAnnotation alan : alcontext.findAnnotation(annotation
174               .getCalcId()))
175       {
176         if (alan.sequenceRef != null
177                 && (alan.label != null && annotation != null && alan.label
178                         .equals(annotation.label)))
179         {
180           if (!rna && alan.isRNA())
181           {
182             rna = true;
183           }
184           seqannot.put(alan.sequenceRef, alan);
185           if (f || alan.graphMax > aamax)
186           {
187             aamax = alan.graphMax;
188           }
189           if (f || alan.graphMin < aamin)
190           {
191             aamin = alan.graphMin;
192           }
193           f = false;
194         }
195       }
196       if (rna)
197       {
198         ColourSchemeProperty.initRnaHelicesShading(1 + (int) aamax);
199       }
200     }
201   }
202
203   float aamin = 0f, aamax = 0f;
204   public String getAnnotation()
205   {
206     return annotation.label;
207   }
208
209   public int getAboveThreshold()
210   {
211     return aboveAnnotationThreshold;
212   }
213
214   public float getAnnotationThreshold()
215   {
216     if (annotationThreshold == null)
217     {
218       return 0;
219     }
220     else
221     {
222       return annotationThreshold.value;
223     }
224   }
225
226   public Color getMinColour()
227   {
228     return new Color((int) r1, (int) g1, (int) b1);
229   }
230
231   public Color getMaxColour()
232   {
233     return new Color((int) (r1 + rr), (int) (g1 + gg), (int) (b1 + bb));
234   }
235
236   /**
237    * DOCUMENT ME!
238    * 
239    * @param n
240    *          DOCUMENT ME!
241    * 
242    * @return DOCUMENT ME!
243    */
244   public Color findColour(char c)
245   {
246     return Color.red;
247   }
248
249   /**
250    * DOCUMENT ME!
251    * 
252    * @param n
253    *          DOCUMENT ME!
254    * @param j
255    *          DOCUMENT ME!
256    * 
257    * @return DOCUMENT ME!
258    */
259   @Override
260   public Color findColour(char c, int j, SequenceI seq)
261   {
262     Color currentColour = Color.white;
263     AlignmentAnnotation annotation = (seqAssociated && seqannot!=null ? seqannot.get(seq)
264             : this.annotation);
265     if (annotation == null)
266     {
267       return currentColour;
268     }
269     if ((threshold == 0) || aboveThreshold(c, j))
270     {
271       if (annotation.annotations != null
272               && j < annotation.annotations.length
273               && annotation.annotations[j] != null
274               && !Comparison.isGap(c))
275       {
276         Annotation aj = annotation.annotations[j];
277         // 'use original colours' => colourScheme != null
278         // -> look up colour to be used
279         // predefined colours => preconfigured shading
280         // -> only use original colours reference if thresholding enabled &
281         // minmax exists
282         // annotation.hasIcons => null or black colours replaced with glyph
283         // colours
284         // -> reuse original colours if present
285         // -> if thresholding enabled then return colour on non-whitespace glyph
286
287         if (aboveAnnotationThreshold == NO_THRESHOLD
288                 || (annotationThreshold != null && (aboveAnnotationThreshold == ABOVE_THRESHOLD ? aj.value >= annotationThreshold.value
289                         : aj.value <= annotationThreshold.value)))
290         {
291           if (predefinedColours && aj.colour != null
292                   && !aj.colour.equals(Color.black))
293           {
294             currentColour = aj.colour;
295           }
296           else if (annotation.hasIcons
297                   && annotation.graph == AlignmentAnnotation.NO_GRAPH)
298           {
299             if (aj.secondaryStructure > ' ' && aj.secondaryStructure != '.'
300                     && aj.secondaryStructure != '-')
301             {
302               if (colourScheme != null)
303               {
304                 currentColour = colourScheme.findColour(c, j, seq);
305               }
306               else
307               {
308                 if (annotation.isRNA())
309                 {
310                   currentColour = ColourSchemeProperty.rnaHelices[(int) aj.value];
311                 }
312                 else
313                 {
314                   currentColour = annotation.annotations[j].secondaryStructure == 'H' ? AnnotationRenderer.HELIX_COLOUR
315                           : annotation.annotations[j].secondaryStructure == 'E' ? AnnotationRenderer.SHEET_COLOUR
316                                   : AnnotationRenderer.STEM_COLOUR;
317                 }
318               }
319             }
320             else
321             {
322               //
323               return Color.white;
324             }
325           }
326           else if (noGradient)
327           {
328             if (colourScheme != null)
329             {
330               currentColour = colourScheme.findColour(c, j, seq);
331             }
332             else
333             {
334               if (aj.colour != null)
335               {
336                 currentColour = aj.colour;
337               }
338             }
339           }
340           else
341           {
342             currentColour = shadeCalculation(annotation, j);
343           }
344         }
345         if (conservationColouring)
346         {
347           currentColour = applyConservation(currentColour, j);
348         }
349       }
350     }
351     return currentColour;
352   }
353
354   private Color shadeCalculation(AlignmentAnnotation annotation, int j)
355   {
356
357     // calculate a shade
358     float range = 1f;
359     if (thresholdIsMinMax
360             && annotation.threshold != null
361             && aboveAnnotationThreshold == ABOVE_THRESHOLD
362             && annotation.annotations[j].value >= annotation.threshold.value)
363     {
364       range = (annotation.annotations[j].value - annotation.threshold.value)
365               / (annotation.graphMax - annotation.threshold.value);
366     }
367     else if (thresholdIsMinMax && annotation.threshold != null
368             && aboveAnnotationThreshold == BELOW_THRESHOLD
369             && annotation.annotations[j].value >= annotation.graphMin)
370     {
371       range = (annotation.annotations[j].value - annotation.graphMin)
372               / (annotation.threshold.value - annotation.graphMin);
373     }
374     else
375     {
376       if (annotation.graphMax != annotation.graphMin)
377       {
378         range = (annotation.annotations[j].value - annotation.graphMin)
379                 / (annotation.graphMax - annotation.graphMin);
380       }
381       else
382       {
383         range = 0f;
384       }
385     }
386
387     int dr = (int) (rr * range + r1), dg = (int) (gg * range + g1), db = (int) (bb
388             * range + b1);
389
390     return new Color(dr, dg, db);
391
392   }
393   public boolean isPredefinedColours()
394   {
395     return predefinedColours;
396   }
397
398   public void setPredefinedColours(boolean predefinedColours)
399   {
400     this.predefinedColours = predefinedColours;
401   }
402
403   public boolean isSeqAssociated()
404   {
405     return seqAssociated;
406   }
407
408   public void setSeqAssociated(boolean sassoc)
409   {
410     seqAssociated = sassoc;
411   }
412 }