f629d091558f3afab005c7be515a585892885408
[jalview.git] / src / jalview / schemes / GraduatedColor.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.api.FeatureColourI;
24 import jalview.datamodel.SequenceFeature;
25
26 import java.awt.Color;
27
28 /**
29  * Value and/or thresholded colour scale used for colouring by annotation and
30  * feature score
31  * 
32  * @author JimP
33  * 
34  */
35 public class GraduatedColor
36 {
37   private static final Color[] colours = new Color[] { Color.blue,
38       Color.red };
39
40   private static int colourModulus = 0;
41
42   int thresholdState = AnnotationColourGradient.NO_THRESHOLD; // or
43                                                               // ABOVE_THRESHOLD
44                                                               // or
45                                                               // BELOW_THRESHOLD
46
47   float lr, lg, lb, dr, dg, db;
48
49   /**
50    * linear scaling parameters, base, minimum colour threshold, range of linear
51    * scale from lower to upper
52    */
53   float base, range, thrsh;
54
55   /**
56    * when true, colour from u to u-d rather than u to u+d
57    */
58   boolean tolow = false;
59
60   /**
61    * when false, min/max range has been manually set so should not be
62    * dynamically adjusted.
63    */
64   boolean autoScale = true;
65
66   /**
67    * construct a graduatedColor object from simple parameters
68    * 
69    * @param low
70    * @param high
71    * @param min
72    * @param max
73    *          color low->high from min->max
74    */
75   public GraduatedColor(Color low, Color high, float min, float max)
76   {
77     thrsh = Float.NaN;
78     tolow = min >= max;
79     lr = low.getRed() / 255f;
80     lg = low.getGreen() / 255f;
81     lb = low.getBlue() / 255f;
82     dr = (high.getRed() / 255f) - lr;
83     dg = (high.getGreen() / 255f) - lg;
84     db = (high.getBlue() / 255f) - lb;
85     if (tolow)
86     {
87       base = max;
88       range = min - max;
89     }
90     else
91     {
92       base = min;
93       range = max - min;
94     }
95   }
96
97   public GraduatedColor(GraduatedColor oldcs)
98   {
99     lr = oldcs.lr;
100     lg = oldcs.lg;
101     lb = oldcs.lb;
102     dr = oldcs.dr;
103     dg = oldcs.dg;
104     db = oldcs.db;
105     base = oldcs.base;
106     range = oldcs.range;
107     tolow = oldcs.tolow;
108     thresholdState = oldcs.thresholdState;
109     thrsh = oldcs.thrsh;
110     autoScale = oldcs.autoScale;
111     colourByLabel = oldcs.colourByLabel;
112     colourAlternately = oldcs.colourAlternately;
113   }
114
115   /**
116    * make a new gradient from an old one with a different scale range
117    * 
118    * @param oldcs
119    * @param min
120    * @param max
121    */
122   public GraduatedColor(GraduatedColor oldcs, float min, float max)
123   {
124     this(oldcs);
125     updateBounds(min, max);
126   }
127
128   public GraduatedColor(FeatureColourI col)
129   {
130     setColourByLabel(col.isColourByLabel());
131   }
132
133   public Color getMinColor()
134   {
135     return new Color(lr, lg, lb);
136   }
137
138   public Color getMaxColor()
139   {
140     return new Color(lr + dr, lg + dg, lb + db);
141   }
142
143   /**
144    * 
145    * @return true if original min/max scale was from high to low
146    */
147   public boolean getTolow()
148   {
149     return tolow;
150   }
151
152   public void setTolow(boolean tolower)
153   {
154     tolow = tolower;
155   }
156
157   public boolean isColored(SequenceFeature feature)
158   {
159     float val = feature.getScore();
160     if (Float.isNaN(val))
161     {
162       return true;
163     }
164     if (this.thresholdState == AnnotationColourGradient.NO_THRESHOLD)
165     {
166       return true;
167     }
168     if (Float.isNaN(this.thrsh))
169     {
170       return true;
171     }
172     boolean rtn = thresholdState == AnnotationColourGradient.ABOVE_THRESHOLD;
173     if (val <= thrsh)
174     {
175       return !rtn; // ? !tolow : tolow;
176     }
177     else
178     {
179       return rtn; // ? tolow : !tolow;
180     }
181   }
182
183   /**
184    * default implementor of a getColourFromString method. TODO: abstract an
185    * interface enabling pluggable colour from string
186    */
187   private UserColourScheme ucs = null;
188
189   private boolean colourByLabel = false;
190
191   private boolean colourAlternately = false;
192
193   /**
194    * 
195    * @return true if colourByLabel style is set
196    */
197   public boolean isColourByLabel()
198   {
199     return colourByLabel;
200   }
201
202   /**
203    * @param colourByLabel
204    *          the colourByLabel to set
205    */
206   public void setColourByLabel(boolean colourByLabel)
207   {
208     this.colourByLabel = colourByLabel;
209   }
210
211   public Color findColor(SequenceFeature feature)
212   {
213     if (colourByLabel)
214     {
215       // TODO: allow user defined feature label colourschemes. Colour space is
216       // {type,regex,%anytype%}x{description string, regex, keyword}
217       if (ucs == null)
218       {
219         ucs = new UserColourScheme();
220       }
221       return ucs.createColourFromName(feature.getDescription());
222     }
223     if (colourAlternately)
224     {
225       int minOrMax = feature.getFeatureNumber() % 2;
226       return minOrMax == 0 ? new Color(lr, lg, lb) : new Color(lr + dr, lg
227               + dg, lb + db);
228     }
229     if (range == 0.0)
230     {
231       return getMaxColor();
232     }
233     float scr = feature.getScore();
234     if (Float.isNaN(scr))
235     {
236       return getMinColor();
237     }
238     float scl = (scr - base) / range;
239     if (tolow)
240     {
241       scl = -scl;
242     }
243     if (scl < 0f)
244     {
245       scl = 0f;
246     }
247     if (scl > 1f)
248     {
249       scl = 1f;
250     }
251     return new Color(lr + scl * dr, lg + scl * dg, lb + scl * db);
252   }
253
254   public void setThresh(float value)
255   {
256     thrsh = value;
257   }
258
259   public float getThresh()
260   {
261     return thrsh;
262   }
263
264   public void setThreshType(int aboveThreshold)
265   {
266     thresholdState = aboveThreshold;
267   }
268
269   public int getThreshType()
270   {
271     return thresholdState;
272   }
273
274   public float getMax()
275   {
276     // regenerate the original values passed in to the constructor
277     return (tolow) ? base : (base + range);
278   }
279
280   public float getMin()
281   {
282     // regenerate the original value passed in to the constructor
283     return (tolow) ? (base + range) : base;
284   }
285
286   public boolean isAutoScale()
287   {
288     return autoScale;
289   }
290
291   public void setAutoScaled(boolean autoscale)
292   {
293     autoScale = autoscale;
294   }
295
296   /**
297    * update the base and range appropriatly for the given minmax range
298    * 
299    * @param a
300    *          float[] {min,max} array containing minmax range for the associated
301    *          score values
302    */
303   public void updateBounds(float min, float max)
304   {
305     if (max < min)
306     {
307       base = max;
308       range = min - max;
309       tolow = true;
310     }
311     else
312     {
313       base = min;
314       range = max - min;
315       tolow = false;
316     }
317   }
318
319   public boolean isColourAlternately()
320   {
321     return colourAlternately;
322   }
323
324   public void setColourAlternately(boolean colourAlternately)
325   {
326     this.colourAlternately = colourAlternately;
327   }
328 }