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 float _255F = 255f;
38 private static final String ABOVE = "above";
40 private static final String BELOW = "below";
42 private static final String ABSO = "abso";
44 private static final String LABEL = "label";
46 private static final String PIPE = "|";
48 private final Color colour;
50 private final Color minColour;
52 private final Color maxColour;
54 private boolean graduatedColour;
56 private boolean colourByLabel;
58 private float threshold;
64 private boolean belowThreshold;
66 private boolean aboveThreshold;
68 private boolean thresholdIsMinOrMax;
70 private boolean isHighToLow;
72 private boolean autoScaled;
74 private final float minRed;
76 private final float minGreen;
78 private final float minBlue;
80 private final float deltaRed;
82 private final float deltaGreen;
84 private final float deltaBlue;
89 public FeatureColour()
95 * Constructor given a simple colour
99 public FeatureColour(Color c)
101 minColour = Color.WHITE;
102 maxColour = Color.BLACK;
113 * Constructor given a colour range and a score range
120 public FeatureColour(Color low, Color high, float min, float max)
122 graduatedColour = true;
124 minColour = low == null ? Color.WHITE : low;
125 maxColour = high == null ? Color.BLACK : high;
126 threshold = Float.NaN;
127 isHighToLow = min >= max;
128 minRed = minColour.getRed() / _255F;
129 minGreen = minColour.getGreen() / _255F;
130 minBlue = minColour.getBlue() / _255F;
131 deltaRed = (maxColour.getRed() / _255F) - minRed;
132 deltaGreen = (maxColour.getGreen() / _255F) - minGreen;
133 deltaBlue = (maxColour.getBlue() / _255F) - minBlue;
151 public FeatureColour(FeatureColour fc)
153 graduatedColour = fc.graduatedColour;
155 minColour = fc.minColour;
156 maxColour = fc.maxColour;
158 minGreen = fc.minGreen;
159 minBlue = fc.minBlue;
160 deltaRed = fc.deltaRed;
161 deltaGreen = fc.deltaGreen;
162 deltaBlue = fc.deltaBlue;
165 isHighToLow = fc.isHighToLow;
166 setAboveThreshold(fc.isAboveThreshold());
167 setBelowThreshold(fc.isBelowThreshold());
168 setThreshold(fc.getThreshold());
169 setAutoScaled(fc.isAutoScaled());
170 setColourByLabel(fc.isColourByLabel());
174 * Copy constructor with new min/max ranges
180 public FeatureColour(FeatureColour fc, float min, float max)
183 graduatedColour = true;
184 updateBounds(min, max);
188 * Parses a Jalview features file format colour descriptor
189 * [label|][mincolour|maxcolour
190 * |[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue] Examples:
194 * <li>25,125,213</li>
196 * <li>label|||0.0|0.0|above|12.5</li>
197 * <li>label|||0.0|0.0|below|12.5</li>
198 * <li>red|green|12.0|26.0|none</li>
199 * <li>a28bbb|3eb555|12.0|26.0|above|12.5</li>
200 * <li>a28bbb|3eb555|abso|12.0|26.0|below|12.5</li>
205 * @throws IllegalArgumentException
208 public static FeatureColour parseJalviewFeatureColour(String descriptor)
210 StringTokenizer gcol = new StringTokenizer(descriptor, PIPE, true);
211 float min = Float.MIN_VALUE;
212 float max = Float.MAX_VALUE;
213 boolean labelColour = false;
215 String mincol = gcol.nextToken();
218 throw new IllegalArgumentException(
219 "Expected either 'label' or a colour specification in the line: "
222 String maxcol = null;
223 if (mincol.toLowerCase().indexOf(LABEL) == 0)
226 mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
228 mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
231 if (!labelColour && !gcol.hasMoreTokens())
234 * only a simple colour specification - parse it
236 Color colour = ColorUtils.parseColourString(descriptor);
239 throw new IllegalArgumentException(
240 "Invalid colour descriptor: " + descriptor);
242 return new FeatureColour(colour);
246 * autoScaled == true: colours range over actual score range
247 * autoScaled == false ('abso'): colours range over min/max range
249 boolean autoScaled = true;
255 // at least four more tokens
256 if (mincol.equals(PIPE))
262 gcol.nextToken(); // skip next '|'
264 maxcol = gcol.nextToken();
265 if (maxcol.equals(PIPE))
271 gcol.nextToken(); // skip next '|'
273 tok = gcol.nextToken();
274 gcol.nextToken(); // skip next '|'
275 if (tok.toLowerCase().startsWith(ABSO))
277 minval = gcol.nextToken();
278 gcol.nextToken(); // skip next '|'
285 maxval = gcol.nextToken();
286 if (gcol.hasMoreTokens())
288 gcol.nextToken(); // skip next '|'
292 if (minval.length() > 0)
294 min = new Float(minval).floatValue();
296 } catch (NumberFormatException e)
298 throw new IllegalArgumentException(
299 "Couldn't parse the minimum value for graduated colour ("
304 if (maxval.length() > 0)
306 max = new Float(maxval).floatValue();
308 } catch (NumberFormatException e)
310 throw new IllegalArgumentException(
311 "Couldn't parse the maximum value for graduated colour ("
317 // add in some dummy min/max colours for the label-only
324 * construct the FeatureColour
326 FeatureColour featureColour;
329 Color minColour = ColorUtils.parseColourString(mincol);
330 Color maxColour = ColorUtils.parseColourString(maxcol);
331 featureColour = new FeatureColour(minColour, maxColour, min, max);
332 featureColour.setColourByLabel(labelColour);
333 featureColour.setAutoScaled(autoScaled);
334 // add in any additional parameters
337 if (gcol.hasMoreTokens())
339 // threshold type and possibly a threshold value
340 ttype = gcol.nextToken();
341 if (ttype.toLowerCase().startsWith(BELOW))
343 featureColour.setBelowThreshold(true);
345 else if (ttype.toLowerCase().startsWith(ABOVE))
347 featureColour.setAboveThreshold(true);
351 if (!ttype.toLowerCase().startsWith("no"))
354 "Ignoring unrecognised threshold type : " + ttype);
358 if (featureColour.hasThreshold())
363 tval = gcol.nextToken();
364 featureColour.setThreshold(new Float(tval).floatValue());
365 } catch (NumberFormatException e)
367 System.err.println("Couldn't parse threshold value as a float: ("
371 if (gcol.hasMoreTokens())
374 "Ignoring additional tokens in parameters in graduated colour specification\n");
375 while (gcol.hasMoreTokens())
377 System.err.println(PIPE + gcol.nextToken());
379 System.err.println("\n");
381 return featureColour;
382 } catch (Exception e)
384 throw new IllegalArgumentException(e.getMessage());
389 public boolean isGraduatedColour()
391 return graduatedColour;
395 * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to
398 void setGraduatedColour(boolean b)
403 setColourByLabel(false);
408 public Color getColour()
414 public Color getMinColour()
420 public Color getMaxColour()
426 public boolean isColourByLabel()
428 return colourByLabel;
432 * Sets the 'colour by label' flag. If true, also sets 'graduated colour' to
436 public void setColourByLabel(boolean b)
441 setGraduatedColour(false);
446 public boolean isBelowThreshold()
448 return belowThreshold;
452 public void setBelowThreshold(boolean b)
457 setAboveThreshold(false);
462 public boolean isAboveThreshold()
464 return aboveThreshold;
468 public void setAboveThreshold(boolean b)
473 setBelowThreshold(false);
478 public boolean isThresholdMinMax()
480 return thresholdIsMinOrMax;
484 public void setThresholdMinMax(boolean b)
486 thresholdIsMinOrMax = b;
490 public float getThreshold()
496 public void setThreshold(float f)
502 public boolean isAutoScaled()
508 public void setAutoScaled(boolean b)
514 * Updates the base and range appropriately for the given minmax range
520 public void updateBounds(float min, float max)
543 public Color getColour(SequenceFeature feature)
545 if (isSimpleColour())
551 * return null if score is outwith any threshold
552 * (for graduated colour or colour by label)
554 float scr = feature.getScore();
555 boolean isNan = Float.isNaN(scr);
558 boolean isAbove = isAboveThreshold() && scr <= threshold;
559 boolean isBelow = !isNan && isBelowThreshold() && scr >= threshold;
560 if (isAbove || isBelow)
566 if (isColourByLabel())
568 return ColorUtils.createColourFromName(feature.getDescription());
575 result = getMinColour();
577 else if (range == 0.0)
579 result = getMaxColour();
583 float scl = (scr - base) / range;
596 result = new Color(minRed + scl * deltaRed,
597 minGreen + scl * deltaGreen, minBlue + scl * deltaBlue);
604 * Returns the maximum score of the graduated colour range
609 public float getMax()
611 // regenerate the original values passed in to the constructor
612 return (isHighToLow) ? base : (base + range);
616 * Returns the minimum score of the graduated colour range
621 public float getMin()
623 // regenerate the original value passed in to the constructor
624 return (isHighToLow) ? (base + range) : base;
628 public boolean isSimpleColour()
630 return (!isColourByLabel() && !isGraduatedColour());
634 public boolean hasThreshold()
636 return isAboveThreshold() || isBelowThreshold();
640 public String toJalviewFormat(String featureType)
642 String colourString = null;
643 if (isSimpleColour())
645 colourString = Format.getHexString(getColour());
649 StringBuilder sb = new StringBuilder(32);
650 if (isColourByLabel())
655 sb.append(PIPE).append(PIPE).append(PIPE);
658 if (isGraduatedColour())
660 sb.append(Format.getHexString(getMinColour())).append(PIPE);
661 sb.append(Format.getHexString(getMaxColour())).append(PIPE);
664 sb.append(ABSO).append(PIPE);
667 if (hasThreshold() || isGraduatedColour())
669 sb.append(getMin()).append(PIPE);
670 sb.append(getMax()).append(PIPE);
671 if (isBelowThreshold())
673 sb.append(BELOW).append(PIPE).append(getThreshold());
675 else if (isAboveThreshold())
677 sb.append(ABOVE).append(PIPE).append(getThreshold());
684 colourString = sb.toString();
686 return String.format("%s\t%s", featureType, colourString);