package jalview.schemes; import jalview.api.FeatureColourI; import jalview.datamodel.SequenceFeature; import java.awt.Color; /** * A class that wraps either a simple colour or a graduated colour */ public class FeatureColour implements FeatureColourI { final private Color colour; final private Color minColour; final private Color maxColour; private boolean graduatedColour; private boolean colourByLabel; private float threshold; private float base; private float range; private boolean belowThreshold; private boolean aboveThreshold; private boolean thresholdIsMinOrMax; private boolean isHighToLow; private boolean autoScaled; final private float minRed; final private float minGreen; final private float minBlue; final private float deltaRed; final private float deltaGreen; final private float deltaBlue; /** * Default constructor */ public FeatureColour() { this((Color) null); } /** * Constructor given a simple colour * * @param c */ public FeatureColour(Color c) { minColour = null; maxColour = null; minRed = 0f; minGreen = 0f; minBlue = 0f; deltaRed = 0f; deltaGreen = 0f; deltaBlue = 0f; colour = c; } /** * Constructor given a colour range and a score range * * @param low * @param high * @param min * @param max */ public FeatureColour(Color low, Color high, float min, float max) { graduatedColour = true; colour = null; minColour = low; maxColour = high; threshold = Float.NaN; isHighToLow = min >= max; minRed = low.getRed() / 255f; minGreen = low.getGreen() / 255f; minBlue = low.getBlue() / 255f; deltaRed = (high.getRed() / 255f) - minRed; deltaGreen = (high.getGreen() / 255f) - minGreen; deltaBlue = (high.getBlue() / 255f) - minBlue; if (isHighToLow) { base = max; range = min - max; } else { base = min; range = max - min; } } /** * Copy constructor * * @param fc */ public FeatureColour(FeatureColour fc) { colour = fc.colour; minColour = fc.minColour; maxColour = fc.maxColour; minRed = fc.minRed; minGreen = fc.minGreen; minBlue = fc.minBlue; deltaRed = fc.deltaRed; deltaGreen = fc.deltaGreen; deltaBlue = fc.deltaBlue; base = fc.base; range = fc.range; isHighToLow = fc.isHighToLow; setAboveThreshold(fc.isAboveThreshold()); setBelowThreshold(fc.isBelowThreshold()); setThreshold(fc.getThreshold()); setAutoScaled(fc.isAutoScaled()); setColourByLabel(fc.isColourByLabel()); } /** * Copy constructor with new min/max ranges * @param fc * @param min * @param max */ public FeatureColour(FeatureColour fc, float min, float max) { this(fc); graduatedColour = true; updateBounds(min, max); } @Override public boolean isGraduatedColour() { return graduatedColour; } /** * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to * false. */ @Override public void setGraduatedColour(boolean b) { graduatedColour = b; if (b) { setColourByLabel(false); } } @Override public Color getColour() { return colour; } @Override public Color getMinColour() { return minColour; } @Override public Color getMaxColour() { return maxColour; } @Override public boolean isColourByLabel() { return colourByLabel; } /** * Sets the 'colour by label' flag. If true, also sets 'graduated colour' to * false. */ @Override public void setColourByLabel(boolean b) { colourByLabel = b; if (b) { setGraduatedColour(false); } } @Override public boolean isBelowThreshold() { return belowThreshold; } @Override public void setBelowThreshold(boolean b) { belowThreshold = b; if (b) { setAboveThreshold(false); } } @Override public boolean isAboveThreshold() { return aboveThreshold; } @Override public void setAboveThreshold(boolean b) { aboveThreshold = b; if (b) { setBelowThreshold(false); } } @Override public boolean isThresholdMinMax() { return thresholdIsMinOrMax; } @Override public void setThresholdMinMax(boolean b) { thresholdIsMinOrMax = b; } @Override public float getThreshold() { return threshold; } @Override public void setThreshold(float f) { threshold = f; } @Override public boolean isAutoScaled() { return autoScaled; } @Override public void setAutoScaled(boolean b) { this.autoScaled = b; } /** * Updates the base and range appropriately for the given minmax range * * @param min * @param max */ @Override public void updateBounds(float min, float max) { if (max < min) { base = max; range = min - max; isHighToLow = true; } else { base = min; range = max - min; isHighToLow = false; } } /** * Returns the colour for the given instance of the feature. This may be a * simple colour, a colour generated from the feature description (if * isColourByLabel()), or a colour derived from the feature score (if * isGraduatedColour()). * * @param feature * @return */ @Override public Color getColor(SequenceFeature feature) { if (isColourByLabel()) { return UserColourScheme .createColourFromName(feature.getDescription()); } if (!isGraduatedColour()) { return getColour(); } // todo should we check for above/below threshold here? if (range == 0.0) { return getMaxColour(); } float scr = feature.getScore(); if (Float.isNaN(scr)) { return getMinColour(); } float scl = (scr - base) / range; if (isHighToLow) { scl = -scl; } if (scl < 0f) { scl = 0f; } if (scl > 1f) { scl = 1f; } return new Color(minRed + scl * deltaRed, minGreen + scl * deltaGreen, minBlue + scl * deltaBlue); } /** * Returns the maximum score of the graduated colour range * * @return */ @Override public float getMax() { // regenerate the original values passed in to the constructor return (isHighToLow) ? base : (base + range); } /** * Returns the minimum score of the graduated colour range * * @return */ @Override public float getMin() { // regenerate the original value passed in to the constructor return (isHighToLow) ? (base + range) : base; } /** * Answers true if the feature has a simple colour, or is coloured by label, * or has a graduated colour and the score of this feature instance is within * the range to render (if any), i.e. does not lie below or above any * threshold set. * * @param feature * @return */ @Override public boolean isColored(SequenceFeature feature) { if (isColourByLabel() || !isGraduatedColour()) { return true; } float val = feature.getScore(); if (Float.isNaN(val)) { return true; } if (Float.isNaN(this.threshold)) { return true; } if (isAboveThreshold() && val <= threshold) { return false; } if (isBelowThreshold() && val >= threshold) { return false; } return true; } @Override public boolean isSimpleColour() { return (!isColourByLabel() && !isGraduatedColour()); } @Override public boolean hasThreshold() { return isAboveThreshold() || isBelowThreshold(); } }