/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see
*
* [label|score|[attribute|attributeName]|][mincolour|maxcolour|
* [absolute|]minvalue|maxvalue|[noValueOption|]thresholdtype|thresholdvalue]
*
* 'Score' is optional (default) for a graduated colour. An attribute with
* sub-attribute should be written as (for example) CSQ:Consequence.
* noValueOption is one of noValueMin, noValueMax, noValueNone
* with default noValueMin.
*
* Examples: *
* This sets the colour scheme to 'graduated' by default. Override this if
* wanted by calling setGraduatedColour(false)
for a simple
* colour, or setColourByLabel(true)
for colour by label.
*
* @param myColour
* @param low
* @param high
* @param noValueColour
* @param min
* @param max
*/
public FeatureColour(Color myColour, Color low, Color high,
Color noValueColour, float min, float max)
{
if (low == null)
{
low = Color.white;
}
if (high == null)
{
high = Color.black;
}
colour = myColour;
minColour = low;
maxColour = high;
setGraduatedColour(true);
noColour = noValueColour;
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;
}
}
@Override
public boolean isGraduatedColour()
{
return graduatedColour;
}
/**
* Sets the 'graduated colour' flag. If true, also sets 'colour by label' to
* false.
*/
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 Color getNoColour()
{
return noColour;
}
@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 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;
}
/**
* {@inheritDoc}
*/
@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())
{
String label = attributeName == null ? feature.getDescription()
: feature.getValueAsString(attributeName);
return label == null ? noColour : ColorUtils
.createColourFromName(label);
}
if (!isGraduatedColour())
{
return getColour();
}
/*
* graduated colour case, optionally with threshold
* may be based on feature score on an attribute value
* Float.NaN, or no value, is assigned the 'no value' colour
*/
float scr = feature.getScore();
if (attributeName != null)
{
try
{
String attVal = feature.getValueAsString(attributeName);
scr = Float.valueOf(attVal);
} catch (Throwable e)
{
scr = Float.NaN;
}
}
if (Float.isNaN(scr))
{
return noColour;
}
if (isAboveThreshold() && scr <= threshold)
{
return null;
}
if (isBelowThreshold() && scr >= threshold)
{
return null;
}
if (range == 0.0)
{
return getMaxColour();
}
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;
}
@Override
public boolean isSimpleColour()
{
return (!isColourByLabel() && !isGraduatedColour());
}
@Override
public boolean hasThreshold()
{
return isAboveThreshold() || isBelowThreshold();
}
@Override
public String toJalviewFormat(String featureType)
{
String colourString = null;
if (isSimpleColour())
{
colourString = Format.getHexString(getColour());
}
else
{
StringBuilder sb = new StringBuilder(32);
if (isColourByAttribute())
{
sb.append(ATTRIBUTE).append(BAR);
sb.append(
FeatureMatcher.toAttributeDisplayName(getAttributeName()));
}
else if (isColourByLabel())
{
sb.append(LABEL);
}
else
{
sb.append(SCORE);
}
if (isGraduatedColour())
{
sb.append(BAR).append(Format.getHexString(getMinColour()))
.append(BAR);
sb.append(Format.getHexString(getMaxColour())).append(BAR);
/*
* 'no value' colour should be null, min or max colour;
* if none of these, coerce to minColour
*/
String noValue = NO_VALUE_MIN;
if (maxColour.equals(noColour))
{
noValue = NO_VALUE_MAX;
}
if (noColour == null)
{
noValue = NO_VALUE_NONE;
}
sb.append(noValue).append(BAR);
if (!isAutoScaled())
{
sb.append(ABSOLUTE).append(BAR);
}
}
else
{
/*
* colour by text with score threshold: empty fields for
* minColour and maxColour (not used)
*/
if (hasThreshold())
{
sb.append(BAR).append(BAR).append(BAR);
}
}
if (hasThreshold() || isGraduatedColour())
{
sb.append(getMin()).append(BAR);
sb.append(getMax()).append(BAR);
if (isBelowThreshold())
{
sb.append(BELOW).append(BAR).append(getThreshold());
}
else if (isAboveThreshold())
{
sb.append(ABOVE).append(BAR).append(getThreshold());
}
else
{
sb.append("none");
}
}
colourString = sb.toString();
}
return String.format("%s\t%s", featureType, colourString);
}
@Override
public boolean isColourByAttribute()
{
return attributeName != null;
}
@Override
public String[] getAttributeName()
{
return attributeName;
}
@Override
public void setAttributeName(String... name)
{
attributeName = name;
}
}