JAL-2360 structure viewers now using ColourMenuHelper, obsolete methods
[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             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   @Override
254   public Color findColour(char c)
255   {
256     return Color.red;
257   }
258
259   /**
260    * DOCUMENT ME!
261    * 
262    * @param n
263    *          DOCUMENT ME!
264    * @param j
265    *          DOCUMENT ME!
266    * 
267    * @return DOCUMENT ME!
268    */
269   @Override
270   public Color findColour(char c, int j, SequenceI seq)
271   {
272     Color currentColour = Color.white;
273     AlignmentAnnotation annotation = (seqAssociated && seqannot != null ? seqannot
274             .get(seq) : this.annotation);
275     if (annotation == null)
276     {
277       return currentColour;
278     }
279     if ((threshold == 0) || aboveThreshold(c, j))
280     {
281       if (annotation.annotations != null
282               && 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 (colourScheme != null)
313               {
314                 currentColour = colourScheme.findColour(c, j, seq);
315               }
316               else
317               {
318                 if (annotation.isRNA())
319                 {
320                   currentColour = ColourSchemeProperty.rnaHelices[(int) aj.value];
321                 }
322                 else
323                 {
324                   currentColour = annotation.annotations[j].secondaryStructure == 'H' ? jalview.renderer.AnnotationRenderer.HELIX_COLOUR
325                           : annotation.annotations[j].secondaryStructure == 'E' ? jalview.renderer.AnnotationRenderer.SHEET_COLOUR
326                                   : jalview.renderer.AnnotationRenderer.STEM_COLOUR;
327                 }
328               }
329             }
330             else
331             {
332               //
333               return Color.white;
334             }
335           }
336           else if (noGradient)
337           {
338             if (colourScheme != null)
339             {
340               currentColour = colourScheme.findColour(c, j, seq);
341             }
342             else
343             {
344               if (aj.colour != null)
345               {
346                 currentColour = aj.colour;
347               }
348             }
349           }
350           else
351           {
352             currentColour = shadeCalculation(annotation, j);
353           }
354         }
355         if (conservationColouring)
356         {
357           currentColour = applyConservation(currentColour, j);
358         }
359       }
360     }
361     return currentColour;
362   }
363
364   private Color shadeCalculation(AlignmentAnnotation annotation, int j)
365   {
366
367     // calculate a shade
368     float range = 1f;
369     if (thresholdIsMinMax
370             && annotation.threshold != null
371             && aboveAnnotationThreshold == ABOVE_THRESHOLD
372             && annotation.annotations[j].value >= annotation.threshold.value)
373     {
374       range = (annotation.annotations[j].value - annotation.threshold.value)
375               / (annotation.graphMax - annotation.threshold.value);
376     }
377     else if (thresholdIsMinMax && annotation.threshold != null
378             && aboveAnnotationThreshold == BELOW_THRESHOLD
379             && annotation.annotations[j].value >= annotation.graphMin)
380     {
381       range = (annotation.annotations[j].value - annotation.graphMin)
382               / (annotation.threshold.value - annotation.graphMin);
383     }
384     else
385     {
386       if (annotation.graphMax != annotation.graphMin)
387       {
388         range = (annotation.annotations[j].value - annotation.graphMin)
389                 / (annotation.graphMax - annotation.graphMin);
390       }
391       else
392       {
393         range = 0f;
394       }
395     }
396
397     int dr = (int) (rr * range + r1), dg = (int) (gg * range + g1), db = (int) (bb
398             * range + b1);
399
400     return new Color(dr, dg, db);
401
402   }
403
404   public boolean isPredefinedColours()
405   {
406     return predefinedColours;
407   }
408
409   public void setPredefinedColours(boolean predefinedColours)
410   {
411     this.predefinedColours = predefinedColours;
412   }
413
414   public boolean isSeqAssociated()
415   {
416     return seqAssociated;
417   }
418
419   public void setSeqAssociated(boolean sassoc)
420   {
421     seqAssociated = sassoc;
422   }
423
424   @Override
425   public String getSchemeName()
426   {
427     return "Annotation";
428   }
429
430   @Override
431   public boolean isSimple()
432   {
433     return false;
434   }
435 }