2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.schemes;
23 import jalview.api.FeatureColourI;
24 import jalview.datamodel.SequenceFeature;
25 import jalview.util.Format;
27 import java.awt.Color;
28 import java.util.StringTokenizer;
31 * A class that wraps either a simple colour or a graduated colour
33 public class FeatureColour implements FeatureColourI
35 private static final String BAR = "|";
37 final private Color colour;
39 final private Color minColour;
41 final private Color maxColour;
43 private boolean graduatedColour;
45 private boolean colourByLabel;
47 private float threshold;
53 private boolean belowThreshold;
55 private boolean aboveThreshold;
57 private boolean thresholdIsMinOrMax;
59 private boolean isHighToLow;
61 private boolean autoScaled;
63 final private float minRed;
65 final private float minGreen;
67 final private float minBlue;
69 final private float deltaRed;
71 final private float deltaGreen;
73 final private float deltaBlue;
76 * Parses a Jalview features file format colour descriptor
77 * [label|][mincolour|maxcolour
78 * |[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue] Examples:
84 * <li>label|||0.0|0.0|above|12.5</li>
85 * <li>label|||0.0|0.0|below|12.5</li>
86 * <li>red|green|12.0|26.0|none</li>
87 * <li>a28bbb|3eb555|12.0|26.0|above|12.5</li>
88 * <li>a28bbb|3eb555|abso|12.0|26.0|below|12.5</li>
93 * @throws IllegalArgumentException
96 public static FeatureColour parseJalviewFeatureColour(String descriptor)
98 StringTokenizer gcol = new StringTokenizer(descriptor, "|", true);
99 float min = Float.MIN_VALUE;
100 float max = Float.MAX_VALUE;
101 boolean labelColour = false;
103 String mincol = gcol.nextToken();
106 throw new IllegalArgumentException(
107 "Expected either 'label' or a colour specification in the line: "
110 String maxcol = null;
111 if (mincol.toLowerCase().indexOf("label") == 0)
114 mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
116 mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
119 if (!labelColour && !gcol.hasMoreTokens())
122 * only a simple colour specification - parse it
124 Color colour = UserColourScheme.getColourFromString(descriptor);
127 throw new IllegalArgumentException("Invalid colour descriptor: "
130 return new FeatureColour(colour);
134 * autoScaled == true: colours range over actual score range
135 * autoScaled == false ('abso'): colours range over min/max range
137 boolean autoScaled = true;
138 String tok = null, minval, maxval;
141 // at least four more tokens
142 if (mincol.equals("|"))
148 gcol.nextToken(); // skip next '|'
150 maxcol = gcol.nextToken();
151 if (maxcol.equals("|"))
157 gcol.nextToken(); // skip next '|'
159 tok = gcol.nextToken();
160 gcol.nextToken(); // skip next '|'
161 if (tok.toLowerCase().startsWith("abso"))
163 minval = gcol.nextToken();
164 gcol.nextToken(); // skip next '|'
171 maxval = gcol.nextToken();
172 if (gcol.hasMoreTokens())
174 gcol.nextToken(); // skip next '|'
178 if (minval.length() > 0)
180 min = new Float(minval).floatValue();
182 } catch (Exception e)
184 throw new IllegalArgumentException(
185 "Couldn't parse the minimum value for graduated colour ("
190 if (maxval.length() > 0)
192 max = new Float(maxval).floatValue();
194 } catch (Exception e)
196 throw new IllegalArgumentException(
197 "Couldn't parse the maximum value for graduated colour ("
203 // add in some dummy min/max colours for the label-only
210 * construct the FeatureColour
212 FeatureColour featureColour;
215 featureColour = new FeatureColour(
216 new UserColourScheme(mincol).findColour('A'),
217 new UserColourScheme(maxcol).findColour('A'), min, max);
218 featureColour.setColourByLabel(labelColour);
219 featureColour.setAutoScaled(autoScaled);
220 // add in any additional parameters
221 String ttype = null, tval = null;
222 if (gcol.hasMoreTokens())
224 // threshold type and possibly a threshold value
225 ttype = gcol.nextToken();
226 if (ttype.toLowerCase().startsWith("below"))
228 featureColour.setBelowThreshold(true);
230 else if (ttype.toLowerCase().startsWith("above"))
232 featureColour.setAboveThreshold(true);
236 if (!ttype.toLowerCase().startsWith("no"))
238 System.err.println("Ignoring unrecognised threshold type : "
243 if (featureColour.hasThreshold())
248 tval = gcol.nextToken();
249 featureColour.setThreshold(new Float(tval).floatValue());
250 } catch (Exception e)
252 System.err.println("Couldn't parse threshold value as a float: ("
256 if (gcol.hasMoreTokens())
259 .println("Ignoring additional tokens in parameters in graduated colour specification\n");
260 while (gcol.hasMoreTokens())
262 System.err.println("|" + gcol.nextToken());
264 System.err.println("\n");
266 return featureColour;
267 } catch (Exception e)
269 throw new IllegalArgumentException(e.getMessage());
274 * Default constructor
276 public FeatureColour()
282 * Constructor given a simple colour
286 public FeatureColour(Color c)
288 minColour = Color.WHITE;
289 maxColour = Color.BLACK;
300 * Constructor given a colour range and a score range
307 public FeatureColour(Color low, Color high, float min, float max)
309 graduatedColour = true;
313 threshold = Float.NaN;
314 isHighToLow = min >= max;
315 minRed = low.getRed() / 255f;
316 minGreen = low.getGreen() / 255f;
317 minBlue = low.getBlue() / 255f;
318 deltaRed = (high.getRed() / 255f) - minRed;
319 deltaGreen = (high.getGreen() / 255f) - minGreen;
320 deltaBlue = (high.getBlue() / 255f) - minBlue;
338 public FeatureColour(FeatureColour fc)
340 graduatedColour = fc.graduatedColour;
342 minColour = fc.minColour;
343 maxColour = fc.maxColour;
345 minGreen = fc.minGreen;
346 minBlue = fc.minBlue;
347 deltaRed = fc.deltaRed;
348 deltaGreen = fc.deltaGreen;
349 deltaBlue = fc.deltaBlue;
352 isHighToLow = fc.isHighToLow;
353 setAboveThreshold(fc.isAboveThreshold());
354 setBelowThreshold(fc.isBelowThreshold());
355 setThreshold(fc.getThreshold());
356 setAutoScaled(fc.isAutoScaled());
357 setColourByLabel(fc.isColourByLabel());
361 * Copy constructor with new min/max ranges
367 public FeatureColour(FeatureColour fc, float min, float max)
370 graduatedColour = true;
371 updateBounds(min, max);
375 public boolean isGraduatedColour()
377 return graduatedColour;
381 * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to
384 void setGraduatedColour(boolean b)
389 setColourByLabel(false);
394 public Color getColour()
400 public Color getMinColour()
406 public Color getMaxColour()
412 public boolean isColourByLabel()
414 return colourByLabel;
418 * Sets the 'colour by label' flag. If true, also sets 'graduated colour' to
422 public void setColourByLabel(boolean b)
427 setGraduatedColour(false);
432 public boolean isBelowThreshold()
434 return belowThreshold;
438 public void setBelowThreshold(boolean b)
443 setAboveThreshold(false);
448 public boolean isAboveThreshold()
450 return aboveThreshold;
454 public void setAboveThreshold(boolean b)
459 setBelowThreshold(false);
464 public boolean isThresholdMinMax()
466 return thresholdIsMinOrMax;
470 public void setThresholdMinMax(boolean b)
472 thresholdIsMinOrMax = b;
476 public float getThreshold()
482 public void setThreshold(float f)
488 public boolean isAutoScaled()
494 public void setAutoScaled(boolean b)
500 * Updates the base and range appropriately for the given minmax range
506 public void updateBounds(float min, float max)
523 * Returns the colour for the given instance of the feature. This may be a
524 * simple colour, a colour generated from the feature description (if
525 * isColourByLabel()), or a colour derived from the feature score (if
526 * isGraduatedColour()).
532 public Color getColor(SequenceFeature feature)
534 if (isColourByLabel())
536 return UserColourScheme
537 .createColourFromName(feature.getDescription());
540 if (!isGraduatedColour())
545 // todo should we check for above/below threshold here?
548 return getMaxColour();
550 float scr = feature.getScore();
551 if (Float.isNaN(scr))
553 return getMinColour();
555 float scl = (scr - base) / range;
568 return new Color(minRed + scl * deltaRed, minGreen + scl * deltaGreen,
569 minBlue + scl * deltaBlue);
573 * Returns the maximum score of the graduated colour range
578 public float getMax()
580 // regenerate the original values passed in to the constructor
581 return (isHighToLow) ? base : (base + range);
585 * Returns the minimum score of the graduated colour range
590 public float getMin()
592 // regenerate the original value passed in to the constructor
593 return (isHighToLow) ? (base + range) : base;
597 * Answers true if the feature has a simple colour, or is coloured by label,
598 * or has a graduated colour and the score of this feature instance is within
599 * the range to render (if any), i.e. does not lie below or above any
606 public boolean isColored(SequenceFeature feature)
608 if (isColourByLabel() || !isGraduatedColour())
613 float val = feature.getScore();
614 if (Float.isNaN(val))
618 if (Float.isNaN(this.threshold))
623 if (isAboveThreshold() && val <= threshold)
627 if (isBelowThreshold() && val >= threshold)
635 public boolean isSimpleColour()
637 return (!isColourByLabel() && !isGraduatedColour());
641 public boolean hasThreshold()
643 return isAboveThreshold() || isBelowThreshold();
647 public String toJalviewFormat(String featureType)
649 String colourString = null;
650 if (isSimpleColour())
652 colourString = Format.getHexString(getColour());
656 StringBuilder sb = new StringBuilder(32);
657 if (isColourByLabel())
662 sb.append(BAR).append(BAR).append(BAR);
665 if (isGraduatedColour())
667 sb.append(Format.getHexString(getMinColour())).append(BAR);
668 sb.append(Format.getHexString(getMaxColour())).append(BAR);
671 sb.append("abso").append(BAR);
674 if (hasThreshold() || isGraduatedColour())
676 sb.append(getMin()).append(BAR);
677 sb.append(getMax()).append(BAR);
678 if (isBelowThreshold())
680 sb.append("below").append(BAR).append(getThreshold());
682 else if (isAboveThreshold())
684 sb.append("above").append(BAR).append(getThreshold());
691 colourString = sb.toString();
693 return String.format("%s\t%s", featureType, colourString);