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