package jalview.datamodel.annotations; import java.awt.Color; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; public class AnnotationColouringRanges extends AnnotationColouring { private static List colours = new ArrayList(); private static List values = new ArrayList(); protected static void addValColour(float v, Color c) { values.add(v); colours.add(c); } protected static void addFirstColour(Color c) { colours.add(0, c); } static { // e.g. // addFirstColour(Color.black); -infty..25 = black // addValColour(25,Color.darkGray); 25..50 = darkGray // addValColour(50,Color.gray); 50..75 = gray // addValColour(75,Color.lightGray); 75..100 = lightGray // addValColour(100,Color.white); 100..infty = white } @Override public Color valueToColour(float val) { Color col = null; boolean set = false; for (int i = 0; i < values.size(); i++) { float compareVal = values.get(i); if (colours.size() > i) { col = colours.get(i); } if (val < compareVal) { set = true; break; } } if (!set && colours.size() > values.size()) { col = colours.get(values.size()); } return col; } @Override public List> rangeColours(float val1, float val2) { String cacheKey = cacheKey(val1, val2); if (!valColorsCache.containsKey(cacheKey)) { List> valCols = new ArrayList<>(); float v1 = val1 <= val2 ? val1 : val2; float v2 = val1 <= val2 ? val2 : val1; boolean reversed = val1 > val2; Color col = null; boolean set1 = false; boolean set2 = false; int i = 0; while (i < values.size() && (!set1 || !set2)) { float compareVal = values.get(i); if (colours.size() > i) { col = colours.get(i); } if (!set1 && v1 < compareVal) { // add the initial checkpoint valCols.add(valCol(reversed ? 1f : 0f, col)); set1 = true; } if (!set2 && v2 < compareVal) { // add the final checkpoint valCols.add(valCol(reversed ? 0f : 1f, col)); set2 = true; break; } if (set1) // && !set2 { // add an intermediate checkpoint float v = (compareVal - v1) / (v2 - v1); valCols.add(valCol(reversed ? 1f - v : v, col)); } i++; } if (colours.size() > i) { col = colours.get(i); } // add above the final checkpoint colour(s) if not set if (!set1) { valCols.add(valCol(reversed ? 1f : 0f, col)); set1 = true; } if (!set2) { // add the final checkpoint valCols.add(valCol(reversed ? 0f : 1f, col)); set2 = true; } if (reversed) { Collections.reverse(valCols); } // put in the cache valColorsCache.put(cacheKey, valCols); } return getFromCache(cacheKey); } private Map.Entry valCol(Float v, Color c) { return new AbstractMap.SimpleEntry(v, c); } private Map>> valColorsCache = new HashMap>>(); private List> getFromCache(String key) { // return a copy of the list in case of element order manipulation (e.g. // valCols.remove(0)) return new ArrayList>(valColorsCache.get(key)); } private static String cacheKey(float f1, float f2) { return new StringBuilder().append(Float.hashCode(f1)).append(' ') .append(Float.hashCode(f2)).toString(); } }