Merge branch 'features/JAL-2360colourSchemeApplicability' into features/JAL-2371colle...
[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
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 getInstance(AnnotatedCollectionI sg,
68           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
69   {
70     AnnotationColourGradient acg = new AnnotationColourGradient(annotation,
71             getColourScheme(), 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       setColourScheme(((AnnotationColourGradient) originalColour)
96               .getColourScheme());
97     }
98     else
99     {
100       setColourScheme(originalColour);
101     }
102
103     this.annotation = annotation;
104
105     aboveAnnotationThreshold = aboveThreshold;
106
107     if (aboveThreshold != NO_THRESHOLD && annotation.threshold != null)
108     {
109       annotationThreshold = annotation.threshold;
110     }
111     // clear values so we don't get weird black bands...
112     r1 = 254;
113     g1 = 254;
114     b1 = 254;
115     rr = 0;
116     gg = 0;
117     bb = 0;
118
119     noGradient = true;
120     checkLimits();
121   }
122
123   /**
124    * Creates a new AnnotationColourGradient object.
125    */
126   public AnnotationColourGradient(AlignmentAnnotation annotation,
127           Color minColour, Color maxColour, int aboveThreshold)
128   {
129     this.annotation = annotation;
130
131     aboveAnnotationThreshold = aboveThreshold;
132
133     if (aboveThreshold != NO_THRESHOLD && annotation.threshold != null)
134     {
135       annotationThreshold = annotation.threshold;
136     }
137
138     r1 = minColour.getRed();
139     g1 = minColour.getGreen();
140     b1 = minColour.getBlue();
141
142     rr = maxColour.getRed() - r1;
143     gg = maxColour.getGreen() - g1;
144     bb = maxColour.getBlue() - b1;
145
146     noGradient = false;
147     checkLimits();
148   }
149
150   private void checkLimits()
151   {
152     aamax = annotation.graphMax;
153     aamin = annotation.graphMin;
154     if (annotation.isRNA())
155     {
156       // reset colour palette
157       ColourSchemeProperty.resetRnaHelicesShading();
158       ColourSchemeProperty.initRnaHelicesShading(1 + (int) aamax);
159     }
160   }
161
162   @Override
163   public void alignmentChanged(AnnotatedCollectionI alignment,
164           Map<SequenceI, SequenceCollectionI> hiddenReps)
165   {
166     super.alignmentChanged(alignment, hiddenReps);
167
168     if (seqAssociated && annotation.getCalcId() != null)
169     {
170       if (seqannot != null)
171       {
172         seqannot.clear();
173       }
174       else
175       {
176         seqannot = new IdentityHashMap<SequenceI, AlignmentAnnotation>();
177       }
178       // resolve the context containing all the annotation for the sequence
179       AnnotatedCollectionI alcontext = alignment instanceof AlignmentI ? alignment
180               : alignment.getContext();
181       boolean f = true, rna = false;
182       for (AlignmentAnnotation alan : alcontext.findAnnotation(annotation
183               .getCalcId()))
184       {
185         if (alan.sequenceRef != null
186                 && (alan.label != null && annotation != null && alan.label
187                         .equals(annotation.label)))
188         {
189           if (!rna && alan.isRNA())
190           {
191             rna = true;
192           }
193           seqannot.put(alan.sequenceRef, alan);
194           if (f || alan.graphMax > aamax)
195           {
196             aamax = alan.graphMax;
197           }
198           if (f || alan.graphMin < aamin)
199           {
200             aamin = alan.graphMin;
201           }
202           f = false;
203         }
204       }
205       if (rna)
206       {
207         ColourSchemeProperty.initRnaHelicesShading(1 + (int) aamax);
208       }
209     }
210   }
211
212   float aamin = 0f, aamax = 0f;
213
214   public String getAnnotation()
215   {
216     return annotation.label;
217   }
218
219   public int getAboveThreshold()
220   {
221     return aboveAnnotationThreshold;
222   }
223
224   public float getAnnotationThreshold()
225   {
226     if (annotationThreshold == null)
227     {
228       return 0;
229     }
230     else
231     {
232       return annotationThreshold.value;
233     }
234   }
235
236   public Color getMinColour()
237   {
238     return new Color((int) r1, (int) g1, (int) b1);
239   }
240
241   public Color getMaxColour()
242   {
243     return new Color((int) (r1 + rr), (int) (g1 + gg), (int) (b1 + bb));
244   }
245
246   /**
247    * DOCUMENT ME!
248    * 
249    * @param n
250    *          DOCUMENT ME!
251    * 
252    * @return DOCUMENT ME!
253    */
254   @Override
255   public Color findColour(char c)
256   {
257     return Color.red;
258   }
259
260   /**
261    * DOCUMENT ME!
262    * 
263    * @param n
264    *          DOCUMENT ME!
265    * @param j
266    *          DOCUMENT ME!
267    * 
268    * @return DOCUMENT ME!
269    */
270   @Override
271   public Color findColour(char c, int j, SequenceI seq)
272   {
273     Color currentColour = Color.white;
274     AlignmentAnnotation annotation = (seqAssociated && seqannot != null ? seqannot
275             .get(seq) : this.annotation);
276     if (annotation == null)
277     {
278       return currentColour;
279     }
280     // if ((threshold == 0) || aboveThreshold(c, j))
281     // {
282     if (annotation.annotations != null && j < annotation.annotations.length
283             && annotation.annotations[j] != null
284             && !jalview.util.Comparison.isGap(c))
285     {
286       Annotation aj = annotation.annotations[j];
287       // 'use original colours' => colourScheme != null
288       // -> look up colour to be used
289       // predefined colours => preconfigured shading
290       // -> only use original colours reference if thresholding enabled &
291       // minmax exists
292       // annotation.hasIcons => null or black colours replaced with glyph
293       // colours
294       // -> reuse original colours if present
295       // -> if thresholding enabled then return colour on non-whitespace glyph
296
297       if (aboveAnnotationThreshold == NO_THRESHOLD
298               || (annotationThreshold != null && (aboveAnnotationThreshold == ABOVE_THRESHOLD ? aj.value >= annotationThreshold.value
299                       : aj.value <= annotationThreshold.value)))
300       {
301         if (predefinedColours && aj.colour != null
302                 && !aj.colour.equals(Color.black))
303         {
304           currentColour = aj.colour;
305         }
306         else if (annotation.hasIcons
307                 && annotation.graph == AlignmentAnnotation.NO_GRAPH)
308         {
309           if (aj.secondaryStructure > ' ' && aj.secondaryStructure != '.'
310                   && aj.secondaryStructure != '-')
311           {
312             if (getColourScheme() != null)
313             {
314               currentColour = getColourScheme().findColour(c, j, seq, null,
315                       0f);
316             }
317             else
318             {
319               if (annotation.isRNA())
320               {
321                 currentColour = ColourSchemeProperty.rnaHelices[(int) aj.value];
322               }
323               else
324               {
325                 currentColour = annotation.annotations[j].secondaryStructure == 'H' ? jalview.renderer.AnnotationRenderer.HELIX_COLOUR
326                         : annotation.annotations[j].secondaryStructure == 'E' ? jalview.renderer.AnnotationRenderer.SHEET_COLOUR
327                                 : jalview.renderer.AnnotationRenderer.STEM_COLOUR;
328               }
329             }
330           }
331           else
332           {
333             //
334             return Color.white;
335           }
336         }
337         else if (noGradient)
338         {
339           if (getColourScheme() != null)
340           {
341             currentColour = getColourScheme().findColour(c, j, seq, null,
342                     0f);
343           }
344           else
345           {
346             if (aj.colour != null)
347             {
348               currentColour = aj.colour;
349             }
350           }
351         }
352         else
353         {
354           currentColour = shadeCalculation(annotation, j);
355         }
356       }
357       // if (conservationColouring)
358       // {
359       // currentColour = applyConservation(currentColour, j);
360       // }
361     }
362     // }
363     return currentColour;
364   }
365
366   private Color shadeCalculation(AlignmentAnnotation annotation, int j)
367   {
368
369     // calculate a shade
370     float range = 1f;
371     if (thresholdIsMinMax
372             && annotation.threshold != null
373             && aboveAnnotationThreshold == ABOVE_THRESHOLD
374             && annotation.annotations[j].value >= annotation.threshold.value)
375     {
376       range = (annotation.annotations[j].value - annotation.threshold.value)
377               / (annotation.graphMax - annotation.threshold.value);
378     }
379     else if (thresholdIsMinMax && annotation.threshold != null
380             && aboveAnnotationThreshold == BELOW_THRESHOLD
381             && annotation.annotations[j].value >= annotation.graphMin)
382     {
383       range = (annotation.annotations[j].value - annotation.graphMin)
384               / (annotation.threshold.value - annotation.graphMin);
385     }
386     else
387     {
388       if (annotation.graphMax != annotation.graphMin)
389       {
390         range = (annotation.annotations[j].value - annotation.graphMin)
391                 / (annotation.graphMax - annotation.graphMin);
392       }
393       else
394       {
395         range = 0f;
396       }
397     }
398
399     int dr = (int) (rr * range + r1), dg = (int) (gg * range + g1), db = (int) (bb
400             * range + b1);
401
402     return new Color(dr, dg, db);
403
404   }
405
406   public boolean isPredefinedColours()
407   {
408     return predefinedColours;
409   }
410
411   public void setPredefinedColours(boolean predefinedColours)
412   {
413     this.predefinedColours = predefinedColours;
414   }
415
416   public boolean isSeqAssociated()
417   {
418     return seqAssociated;
419   }
420
421   public void setSeqAssociated(boolean sassoc)
422   {
423     seqAssociated = sassoc;
424   }
425
426   @Override
427   public String getSchemeName()
428   {
429     return "Annotation";
430   }
431
432   @Override
433   public boolean isSimple()
434   {
435     return false;
436   }
437 }