JAL-1807 - Bob's last(?) before leaving Dundee -- adds fast file loading
[jalviewjs.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 findColourSeq(char c, int j, SequenceI seq) {
261                 Color currentColour = Color.white;
262                 AlignmentAnnotation annotation = (seqAssociated && seqannot != null ? seqannot
263                                 .get(seq) : this.annotation);
264                 if (annotation == null) {
265                         return currentColour;
266                 }
267                 if ((threshold == 0) || aboveThreshold(c, j)) {
268                         if (annotation.annotations != null && j < annotation.annotations.length
269                                         && annotation.annotations[j] != null && !Comparison.isGap(c)) {
270                                 Annotation aj = annotation.annotations[j];
271                                 // 'use original colours' => colourScheme != null
272                                 // -> look up colour to be used
273                                 // predefined colours => preconfigured shading
274                                 // -> only use original colours reference if thresholding enabled &
275                                 // minmax exists
276                                 // annotation.hasIcons => null or black colours replaced with glyph
277                                 // colours
278                                 // -> reuse original colours if present
279                                 // -> if thresholding enabled then return colour on non-whitespace glyph
280
281                                 if (aboveAnnotationThreshold == NO_THRESHOLD
282                                                 || (annotationThreshold != null && (aboveAnnotationThreshold == ABOVE_THRESHOLD ? aj.value >= annotationThreshold.value
283                                                                 : aj.value <= annotationThreshold.value))) {
284                                         if (predefinedColours && aj.colour != null
285                                                         && !aj.colour.equals(Color.black)) {
286                                                 currentColour = aj.colour;
287                                         } else if (annotation.hasIcons
288                                                         && annotation.graph == AlignmentAnnotation.NO_GRAPH) {
289                                                 if (aj.secondaryStructure > ' ' && aj.secondaryStructure != '.'
290                                                                 && aj.secondaryStructure != '-') {
291                                                         if (colourScheme != null) {
292                                                                 currentColour = colourScheme.findColourSeq(c, j, seq);
293                                                         } else {
294                                                                 if (annotation.isRNA()) {
295                                                                         currentColour = ColourSchemeProperty.rnaHelices[(int) aj.value];
296                                                                 } else {
297                                                                         currentColour = annotation.annotations[j].secondaryStructure == 'H' ? AnnotationRenderer.HELIX_COLOUR
298                                                                                         : annotation.annotations[j].secondaryStructure == 'E' ? AnnotationRenderer.SHEET_COLOUR
299                                                                                                         : AnnotationRenderer.STEM_COLOUR;
300                                                                 }
301                                                         }
302                                                 } else {
303                                                         //
304                                                         return Color.white;
305                                                 }
306                                         } else if (noGradient) {
307                                                 if (colourScheme != null) {
308                                                         currentColour = colourScheme.findColourSeq(c, j, seq);
309                                                 } else {
310                                                         if (aj.colour != null) {
311                                                                 currentColour = aj.colour;
312                                                         }
313                                                 }
314                                         } else {
315                                                 currentColour = shadeCalculation(annotation, j);
316                                         }
317                                 }
318                                 if (conservationColouring) {
319                                         currentColour = applyConservation(currentColour, j);
320                                 }
321                         }
322                 }
323                 return currentColour;
324         }
325
326   private Color shadeCalculation(AlignmentAnnotation annotation, int j)
327   {
328
329     // calculate a shade
330     float range = 1f;
331     if (thresholdIsMinMax
332             && annotation.threshold != null
333             && aboveAnnotationThreshold == ABOVE_THRESHOLD
334             && annotation.annotations[j].value >= annotation.threshold.value)
335     {
336       range = (annotation.annotations[j].value - annotation.threshold.value)
337               / (annotation.graphMax - annotation.threshold.value);
338     }
339     else if (thresholdIsMinMax && annotation.threshold != null
340             && aboveAnnotationThreshold == BELOW_THRESHOLD
341             && annotation.annotations[j].value >= annotation.graphMin)
342     {
343       range = (annotation.annotations[j].value - annotation.graphMin)
344               / (annotation.threshold.value - annotation.graphMin);
345     }
346     else
347     {
348       if (annotation.graphMax != annotation.graphMin)
349       {
350         range = (annotation.annotations[j].value - annotation.graphMin)
351                 / (annotation.graphMax - annotation.graphMin);
352       }
353       else
354       {
355         range = 0f;
356       }
357     }
358
359     int dr = (int) (rr * range + r1), dg = (int) (gg * range + g1), db = (int) (bb
360             * range + b1);
361
362     return new Color(dr, dg, db);
363
364   }
365   public boolean isPredefinedColours()
366   {
367     return predefinedColours;
368   }
369
370   public void setPredefinedColours(boolean predefinedColours)
371   {
372     this.predefinedColours = predefinedColours;
373   }
374
375   public boolean isSeqAssociated()
376   {
377     return seqAssociated;
378   }
379
380   public void setSeqAssociated(boolean sassoc)
381   {
382     seqAssociated = sassoc;
383   }
384 }