1 package jalview.schemes;
3 import jalview.api.ColorI;
4 import jalview.api.FeatureColourI;
5 import jalview.datamodel.SequenceFeature;
6 import jalview.util.Format;
9 import java.util.StringTokenizer;
12 * A class that wraps either a simple colour or a graduated colour
14 public class FeatureColour implements FeatureColourI
16 private static final String BAR = "|";
18 final private ColorI colour;
20 final private ColorI minColour;
22 final private ColorI maxColour;
24 private boolean graduatedColour;
26 private boolean colourByLabel;
28 private float threshold;
34 private boolean belowThreshold;
36 private boolean aboveThreshold;
38 private boolean thresholdIsMinOrMax;
40 private boolean isHighToLow;
42 private boolean autoScaled;
44 final private float minRed;
46 final private float minGreen;
48 final private float minBlue;
50 final private float deltaRed;
52 final private float deltaGreen;
54 final private float deltaBlue;
57 * Parses a Jalview features file format colour descriptor
58 * [label|][mincolour|maxcolour
59 * |[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue] Examples:
65 * <li>label|||0.0|0.0|above|12.5</li>
66 * <li>label|||0.0|0.0|below|12.5</li>
67 * <li>red|green|12.0|26.0|none</li>
68 * <li>a28bbb|3eb555|12.0|26.0|above|12.5</li>
69 * <li>a28bbb|3eb555|abso|12.0|26.0|below|12.5</li>
74 * @throws IllegalArgumentException
77 public static FeatureColour parseJalviewFeatureColour(String descriptor)
79 StringTokenizer gcol = new StringTokenizer(descriptor, "|", true);
80 float min = Float.MIN_VALUE;
81 float max = Float.MAX_VALUE;
82 boolean labelColour = false;
84 String mincol = gcol.nextToken();
87 throw new IllegalArgumentException(
88 "Expected either 'label' or a colour specification in the line: "
92 if (mincol.toLowerCase().indexOf("label") == 0)
95 mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
97 mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
100 if (!labelColour && !gcol.hasMoreTokens())
103 * only a simple colour specification - parse it
105 Color colour = UserColourScheme.getColourFromString(descriptor);
108 throw new IllegalArgumentException("Invalid colour descriptor: "
111 return new FeatureColour(new Colour(colour));
115 * autoScaled == true: colours range over actual score range; autoScaled ==
116 * false ('abso'): colours range over min/max range
118 boolean autoScaled = false;
119 String tok = null, minval, maxval;
122 // at least four more tokens
123 if (mincol.equals("|"))
129 gcol.nextToken(); // skip next '|'
131 maxcol = gcol.nextToken();
132 if (maxcol.equals("|"))
138 gcol.nextToken(); // skip next '|'
140 tok = gcol.nextToken();
141 gcol.nextToken(); // skip next '|'
142 if (tok.toLowerCase().indexOf("abso") != 0)
149 minval = gcol.nextToken();
150 gcol.nextToken(); // skip next '|'
152 maxval = gcol.nextToken();
153 if (gcol.hasMoreTokens())
155 gcol.nextToken(); // skip next '|'
159 if (minval.length() > 0)
161 min = new Float(minval).floatValue();
163 } catch (Exception e)
165 throw new IllegalArgumentException(
166 "Couldn't parse the minimum value for graduated colour ("
171 if (maxval.length() > 0)
173 max = new Float(maxval).floatValue();
175 } catch (Exception e)
177 throw new IllegalArgumentException(
178 "Couldn't parse the maximum value for graduated colour ("
184 // add in some dummy min/max colours for the label-only
191 * construct the FeatureColour
193 FeatureColour featureColour;
196 featureColour = new FeatureColour(new Colour(new UserColourScheme(
197 mincol).findColour('A')), new Colour(new UserColourScheme(
198 maxcol).findColour('A')), min, max);
199 featureColour.setColourByLabel(labelColour);
200 featureColour.setAutoScaled(autoScaled);
201 // add in any additional parameters
202 String ttype = null, tval = null;
203 if (gcol.hasMoreTokens())
205 // threshold type and possibly a threshold value
206 ttype = gcol.nextToken();
207 if (ttype.toLowerCase().startsWith("below"))
209 featureColour.setBelowThreshold(true);
211 else if (ttype.toLowerCase().startsWith("above"))
213 featureColour.setAboveThreshold(true);
217 if (!ttype.toLowerCase().startsWith("no"))
219 System.err.println("Ignoring unrecognised threshold type : "
224 if (featureColour.hasThreshold())
229 tval = gcol.nextToken();
230 featureColour.setThreshold(new Float(tval).floatValue());
231 } catch (Exception e)
233 System.err.println("Couldn't parse threshold value as a float: ("
237 if (gcol.hasMoreTokens())
240 .println("Ignoring additional tokens in parameters in graduated colour specification\n");
241 while (gcol.hasMoreTokens())
243 System.err.println("|" + gcol.nextToken());
245 System.err.println("\n");
247 return featureColour;
248 } catch (Exception e)
250 throw new IllegalArgumentException(e.getMessage());
255 * Default constructor
257 public FeatureColour()
263 * Constructor given a simple colour
267 public FeatureColour(ColorI c)
269 minColour = new Colour(Color.WHITE);
270 maxColour = new Colour(Color.BLACK);
280 public FeatureColour(Color c)
286 * Constructor given a colour range and a score range
293 public FeatureColour(ColorI low, ColorI high, float min, float max)
295 graduatedColour = true;
299 threshold = Float.NaN;
300 isHighToLow = min >= max;
301 minRed = low.getRed() / 255f;
302 minGreen = low.getGreen() / 255f;
303 minBlue = low.getBlue() / 255f;
304 deltaRed = (high.getRed() / 255f) - minRed;
305 deltaGreen = (high.getGreen() / 255f) - minGreen;
306 deltaBlue = (high.getBlue() / 255f) - minBlue;
319 public FeatureColour(Color low, Color high, float min, float max)
321 this(new Colour(low), new Colour(high), min, max);
328 public FeatureColour(FeatureColour fc)
331 minColour = fc.minColour;
332 maxColour = fc.maxColour;
334 minGreen = fc.minGreen;
335 minBlue = fc.minBlue;
336 deltaRed = fc.deltaRed;
337 deltaGreen = fc.deltaGreen;
338 deltaBlue = fc.deltaBlue;
341 isHighToLow = fc.isHighToLow;
342 setAboveThreshold(fc.isAboveThreshold());
343 setBelowThreshold(fc.isBelowThreshold());
344 setThreshold(fc.getThreshold());
345 setAutoScaled(fc.isAutoScaled());
346 setColourByLabel(fc.isColourByLabel());
350 * Copy constructor with new min/max ranges
355 public FeatureColour(FeatureColour fc, float min, float max)
358 graduatedColour = true;
359 updateBounds(min, max);
363 public boolean isGraduatedColour()
365 return graduatedColour;
369 * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to
373 public void setGraduatedColour(boolean b)
378 setColourByLabel(false);
383 public ColorI getColour()
389 public ColorI getMinColour()
395 public ColorI getMaxColour()
401 public boolean isColourByLabel()
403 return colourByLabel;
407 * Sets the 'colour by label' flag. If true, also sets 'graduated colour' to
411 public void setColourByLabel(boolean b)
416 setGraduatedColour(false);
420 public boolean isBelowThreshold()
422 return belowThreshold;
426 public void setBelowThreshold(boolean b)
431 setAboveThreshold(false);
436 public boolean isAboveThreshold()
438 return aboveThreshold;
442 public void setAboveThreshold(boolean b)
447 setBelowThreshold(false);
452 public boolean isThresholdMinMax()
454 return thresholdIsMinOrMax;
458 public void setThresholdMinMax(boolean b)
460 thresholdIsMinOrMax = b;
464 public float getThreshold()
470 public void setThreshold(float f)
476 public boolean isAutoScaled()
482 public void setAutoScaled(boolean b)
488 * Updates the base and range appropriately for the given minmax range
494 public void updateBounds(float min, float max)
511 * Returns the colour for the given instance of the feature. This may be a
512 * simple colour, a colour generated from the feature description (if
513 * isColourByLabel()), or a colour derived from the feature score (if
514 * isGraduatedColour()).
520 public ColorI getColor(SequenceFeature feature)
522 if (isColourByLabel())
524 return new Colour(UserColourScheme.createColourFromName(feature
528 if (!isGraduatedColour())
533 // todo should we check for above/below threshold here?
536 return getMaxColour();
538 float scr = feature.getScore();
539 if (Float.isNaN(scr))
541 return getMinColour();
543 float scl = (scr - base) / range;
556 return new Colour(new Color(minRed + scl * deltaRed, minGreen + scl
557 * deltaGreen, minBlue + scl * deltaBlue));
561 * Returns the maximum score of the graduated colour range
566 public float getMax()
568 // regenerate the original values passed in to the constructor
569 return (isHighToLow) ? base : (base + range);
573 * Returns the minimum score of the graduated colour range
578 public float getMin()
580 // regenerate the original value passed in to the constructor
581 return (isHighToLow) ? (base + range) : base;
585 * Answers true if the feature has a simple colour, or is coloured by label,
586 * or has a graduated colour and the score of this feature instance is within
587 * the range to render (if any), i.e. does not lie below or above any
594 public boolean isColored(SequenceFeature feature)
596 if (isColourByLabel() || !isGraduatedColour())
601 float val = feature.getScore();
602 if (Float.isNaN(val))
606 if (Float.isNaN(this.threshold))
611 if (isAboveThreshold() && val <= threshold)
615 if (isBelowThreshold() && val >= threshold)
623 public boolean isSimpleColour()
625 return (!isColourByLabel() && !isGraduatedColour());
629 public boolean hasThreshold()
631 return isAboveThreshold() || isBelowThreshold();
635 public String toJalviewFormat(String featureType)
637 String colourString = null;
638 if (isSimpleColour())
640 colourString = Format.getHexString(getColour());
644 StringBuilder sb = new StringBuilder(32);
645 if (isColourByLabel())
650 sb.append(BAR).append(BAR).append(BAR);
653 if (isGraduatedColour())
655 sb.append(Format.getHexString(getMinColour())).append(BAR);
656 sb.append(Format.getHexString(getMaxColour())).append(BAR);
659 sb.append("abso").append(BAR);
662 if (hasThreshold() || isGraduatedColour())
664 sb.append(getMin()).append(BAR);
665 sb.append(getMax()).append(BAR);
666 if (isBelowThreshold())
668 sb.append("below").append(BAR).append(getThreshold());
670 else if (isAboveThreshold())
672 sb.append("above").append(BAR).append(getThreshold());
679 colourString = sb.toString();
681 return String.format("%s\t%s", featureType, colourString);