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.ColorUtils;
26 import jalview.util.Format;
28 import java.awt.Color;
29 import java.util.StringTokenizer;
32 * A class that wraps either a simple colour or a graduated colour
34 public class FeatureColour implements FeatureColourI
36 private static final String BAR = "|";
38 final private Color colour;
40 final private Color minColour;
42 final private Color maxColour;
44 private boolean graduatedColour;
46 private boolean colourByLabel;
48 private float threshold;
54 private boolean belowThreshold;
56 private boolean aboveThreshold;
58 private boolean thresholdIsMinOrMax;
60 private boolean isHighToLow;
62 private boolean autoScaled;
64 final private float minRed;
66 final private float minGreen;
68 final private float minBlue;
70 final private float deltaRed;
72 final private float deltaGreen;
74 final private float deltaBlue;
77 * Parses a Jalview features file format colour descriptor
78 * [label|][mincolour|maxcolour
79 * |[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue] Examples:
85 * <li>label|||0.0|0.0|above|12.5</li>
86 * <li>label|||0.0|0.0|below|12.5</li>
87 * <li>red|green|12.0|26.0|none</li>
88 * <li>a28bbb|3eb555|12.0|26.0|above|12.5</li>
89 * <li>a28bbb|3eb555|abso|12.0|26.0|below|12.5</li>
94 * @throws IllegalArgumentException
97 public static FeatureColour parseJalviewFeatureColour(String descriptor)
99 StringTokenizer gcol = new StringTokenizer(descriptor, "|", true);
100 float min = Float.MIN_VALUE;
101 float max = Float.MAX_VALUE;
102 boolean labelColour = false;
104 String mincol = gcol.nextToken();
107 throw new IllegalArgumentException(
108 "Expected either 'label' or a colour specification in the line: "
111 String maxcol = null;
112 if (mincol.toLowerCase().indexOf("label") == 0)
115 mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
117 mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
120 if (!labelColour && !gcol.hasMoreTokens())
123 * only a simple colour specification - parse it
125 Color colour = ColorUtils.parseColourString(descriptor);
128 throw new IllegalArgumentException("Invalid colour descriptor: "
131 return new FeatureColour(colour);
135 * autoScaled == true: colours range over actual score range
136 * autoScaled == false ('abso'): colours range over min/max range
138 boolean autoScaled = true;
139 String tok = null, minval, maxval;
142 // at least four more tokens
143 if (mincol.equals("|"))
149 gcol.nextToken(); // skip next '|'
151 maxcol = gcol.nextToken();
152 if (maxcol.equals("|"))
158 gcol.nextToken(); // skip next '|'
160 tok = gcol.nextToken();
161 gcol.nextToken(); // skip next '|'
162 if (tok.toLowerCase().startsWith("abso"))
164 minval = gcol.nextToken();
165 gcol.nextToken(); // skip next '|'
172 maxval = gcol.nextToken();
173 if (gcol.hasMoreTokens())
175 gcol.nextToken(); // skip next '|'
179 if (minval.length() > 0)
181 min = new Float(minval).floatValue();
183 } catch (Exception e)
185 throw new IllegalArgumentException(
186 "Couldn't parse the minimum value for graduated colour ("
191 if (maxval.length() > 0)
193 max = new Float(maxval).floatValue();
195 } catch (Exception e)
197 throw new IllegalArgumentException(
198 "Couldn't parse the maximum value for graduated colour ("
204 // add in some dummy min/max colours for the label-only
211 * construct the FeatureColour
213 FeatureColour featureColour;
216 Color minColour = ColorUtils.parseColourString(mincol);
217 Color maxColour = ColorUtils.parseColourString(maxcol);
218 featureColour = new FeatureColour(minColour, maxColour, min, max);
219 featureColour.setColourByLabel(labelColour);
220 featureColour.setAutoScaled(autoScaled);
221 // add in any additional parameters
222 String ttype = null, tval = null;
223 if (gcol.hasMoreTokens())
225 // threshold type and possibly a threshold value
226 ttype = gcol.nextToken();
227 if (ttype.toLowerCase().startsWith("below"))
229 featureColour.setBelowThreshold(true);
231 else if (ttype.toLowerCase().startsWith("above"))
233 featureColour.setAboveThreshold(true);
237 if (!ttype.toLowerCase().startsWith("no"))
239 System.err.println("Ignoring unrecognised threshold type : "
244 if (featureColour.hasThreshold())
249 tval = gcol.nextToken();
250 featureColour.setThreshold(new Float(tval).floatValue());
251 } catch (Exception e)
253 System.err.println("Couldn't parse threshold value as a float: ("
257 if (gcol.hasMoreTokens())
260 .println("Ignoring additional tokens in parameters in graduated colour specification\n");
261 while (gcol.hasMoreTokens())
263 System.err.println("|" + gcol.nextToken());
265 System.err.println("\n");
267 return featureColour;
268 } catch (Exception e)
270 throw new IllegalArgumentException(e.getMessage());
275 * Default constructor
277 public FeatureColour()
283 * Constructor given a simple colour
287 public FeatureColour(Color c)
289 minColour = Color.WHITE;
290 maxColour = Color.BLACK;
301 * Constructor given a colour range and a score range
308 public FeatureColour(Color low, Color high, float min, float max)
318 graduatedColour = true;
322 threshold = Float.NaN;
323 isHighToLow = min >= max;
324 minRed = low.getRed() / 255f;
325 minGreen = low.getGreen() / 255f;
326 minBlue = low.getBlue() / 255f;
327 deltaRed = (high.getRed() / 255f) - minRed;
328 deltaGreen = (high.getGreen() / 255f) - minGreen;
329 deltaBlue = (high.getBlue() / 255f) - minBlue;
347 public FeatureColour(FeatureColour fc)
349 graduatedColour = fc.graduatedColour;
351 minColour = fc.minColour;
352 maxColour = fc.maxColour;
354 minGreen = fc.minGreen;
355 minBlue = fc.minBlue;
356 deltaRed = fc.deltaRed;
357 deltaGreen = fc.deltaGreen;
358 deltaBlue = fc.deltaBlue;
361 isHighToLow = fc.isHighToLow;
362 setAboveThreshold(fc.isAboveThreshold());
363 setBelowThreshold(fc.isBelowThreshold());
364 setThreshold(fc.getThreshold());
365 setAutoScaled(fc.isAutoScaled());
366 setColourByLabel(fc.isColourByLabel());
370 * Copy constructor with new min/max ranges
376 public FeatureColour(FeatureColour fc, float min, float max)
379 graduatedColour = true;
380 updateBounds(min, max);
384 public boolean isGraduatedColour()
386 return graduatedColour;
390 * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to
393 void setGraduatedColour(boolean b)
398 setColourByLabel(false);
403 public Color getColour()
409 public Color getMinColour()
415 public Color getMaxColour()
421 public boolean isColourByLabel()
423 return colourByLabel;
427 * Sets the 'colour by label' flag. If true, also sets 'graduated colour' to
431 public void setColourByLabel(boolean b)
436 setGraduatedColour(false);
441 public boolean isBelowThreshold()
443 return belowThreshold;
447 public void setBelowThreshold(boolean b)
452 setAboveThreshold(false);
457 public boolean isAboveThreshold()
459 return aboveThreshold;
463 public void setAboveThreshold(boolean b)
468 setBelowThreshold(false);
473 public boolean isThresholdMinMax()
475 return thresholdIsMinOrMax;
479 public void setThresholdMinMax(boolean b)
481 thresholdIsMinOrMax = b;
485 public float getThreshold()
491 public void setThreshold(float f)
497 public boolean isAutoScaled()
503 public void setAutoScaled(boolean b)
509 * Updates the base and range appropriately for the given minmax range
515 public void updateBounds(float min, float max)
532 * Returns the colour for the given instance of the feature. This may be a
533 * simple colour, a colour generated from the feature description (if
534 * isColourByLabel()), or a colour derived from the feature score (if
535 * isGraduatedColour()).
541 public Color getColor(SequenceFeature feature)
543 if (isColourByLabel())
546 .createColourFromName(feature.getDescription());
549 if (!isGraduatedColour())
554 // todo should we check for above/below threshold here?
557 return getMaxColour();
559 float scr = feature.getScore();
560 if (Float.isNaN(scr))
562 return getMinColour();
564 float scl = (scr - base) / range;
577 return new Color(minRed + scl * deltaRed, minGreen + scl * deltaGreen,
578 minBlue + scl * deltaBlue);
582 * Returns the maximum score of the graduated colour range
587 public float getMax()
589 // regenerate the original values passed in to the constructor
590 return (isHighToLow) ? base : (base + range);
594 * Returns the minimum score of the graduated colour range
599 public float getMin()
601 // regenerate the original value passed in to the constructor
602 return (isHighToLow) ? (base + range) : base;
606 * Answers true if the feature has a simple colour, or is coloured by label,
607 * or has a graduated colour and the score of this feature instance is within
608 * the range to render (if any), i.e. does not lie below or above any
615 public boolean isColored(SequenceFeature feature)
617 if (isColourByLabel() || !isGraduatedColour())
622 float val = feature.getScore();
623 if (Float.isNaN(val))
627 if (Float.isNaN(this.threshold))
632 if (isAboveThreshold() && val <= threshold)
636 if (isBelowThreshold() && val >= threshold)
644 public boolean isSimpleColour()
646 return (!isColourByLabel() && !isGraduatedColour());
650 public boolean hasThreshold()
652 return isAboveThreshold() || isBelowThreshold();
656 public String toJalviewFormat(String featureType)
658 String colourString = null;
659 if (isSimpleColour())
661 colourString = Format.getHexString(getColour());
665 StringBuilder sb = new StringBuilder(32);
666 if (isColourByLabel())
671 sb.append(BAR).append(BAR).append(BAR);
674 if (isGraduatedColour())
676 sb.append(Format.getHexString(getMinColour())).append(BAR);
677 sb.append(Format.getHexString(getMaxColour())).append(BAR);
680 sb.append("abso").append(BAR);
683 if (hasThreshold() || isGraduatedColour())
685 sb.append(getMin()).append(BAR);
686 sb.append(getMax()).append(BAR);
687 if (isBelowThreshold())
689 sb.append("below").append(BAR).append(getThreshold());
691 else if (isAboveThreshold())
693 sb.append("above").append(BAR).append(getThreshold());
700 colourString = sb.toString();
702 return String.format("%s\t%s", featureType, colourString);