JAL-2965 graduated sequence point colour from back to front
[jalview.git] / src / jalview / util / ColorUtils.java
index d7829df..16ff259 100644 (file)
 package jalview.util;
 
 import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Random;
 
 public class ColorUtils
 {
+  // constant borrowed from java.awt.Color
+  private static final float FACTOR = 0.7f;
+
+  private static final int MAX_CACHE_SIZE = 1729;
+  /*
+   * a cache for colours generated from text strings
+   */
+  static Map<String, Color> myColours = new HashMap<>();
 
   /**
    * Generates a random color, will mix with input color. Code taken from
@@ -134,12 +144,12 @@ public class ColorUtils
      * prop = proportion of the way value is from minValue to maxValue
      */
     float prop = (value - minValue) / (maxValue - minValue);
-    float r = minColour.getRed() + prop
-            * (maxColour.getRed() - minColour.getRed());
-    float g = minColour.getGreen() + prop
-            * (maxColour.getGreen() - minColour.getGreen());
-    float b = minColour.getBlue() + prop
-            * (maxColour.getBlue() - minColour.getBlue());
+    float r = minColour.getRed()
+            + prop * (maxColour.getRed() - minColour.getRed());
+    float g = minColour.getGreen()
+            + prop * (maxColour.getGreen() - minColour.getGreen());
+    float b = minColour.getBlue()
+            + prop * (maxColour.getBlue() - minColour.getBlue());
     return new Color(r / 255, g / 255, b / 255);
   }
 
@@ -210,7 +220,7 @@ public class ColorUtils
       return null;
     }
     colour = colour.trim();
-  
+
     Color col = null;
     try
     {
@@ -219,12 +229,12 @@ public class ColorUtils
     } catch (NumberFormatException ex)
     {
     }
-  
+
     if (col == null)
     {
       col = ColorUtils.getAWTColorFromName(colour);
     }
-  
+
     if (col == null)
     {
       try
@@ -242,7 +252,7 @@ public class ColorUtils
         // non-numeric token or out of 0-255 range
       }
     }
-  
+
     return col;
   }
 
@@ -260,35 +270,46 @@ public class ColorUtils
     {
       return Color.white;
     }
+    if (myColours.containsKey(name))
+    {
+      return myColours.get(name);
+    }
     int lsize = name.length();
     int start = 0;
     int end = lsize / 3;
-  
+
     int rgbOffset = Math.abs(name.hashCode() % 10) * 15; // 0-135
-  
+
     /*
      * red: first third
      */
-    int r = Math.abs(name.substring(start, end).hashCode() + rgbOffset) % 210 + 20;
+    int r = Math.abs(name.substring(start, end).hashCode() + rgbOffset)
+            % 210 + 20;
     start = end;
     end += lsize / 3;
     if (end > lsize)
     {
       end = lsize;
     }
-  
+
     /*
      * green: second third
      */
-    int g = Math.abs(name.substring(start, end).hashCode() + rgbOffset) % 210 + 20;
-  
+    int g = Math.abs(name.substring(start, end).hashCode() + rgbOffset)
+            % 210 + 20;
+
     /*
      * blue: third third
      */
     int b = Math.abs(name.substring(end).hashCode() + rgbOffset) % 210 + 20;
-  
+
     Color color = new Color(r, g, b);
-  
+
+    if (myColours.size() < MAX_CACHE_SIZE)
+    {
+      myColours.put(name, color);
+    }
+
     return color;
   }
 
@@ -307,7 +328,7 @@ public class ColorUtils
     }
     Color col = null;
     name = name.toLowerCase();
-  
+
     // or make a static map; or use reflection on the field name
     switch (name)
     {
@@ -351,7 +372,66 @@ public class ColorUtils
       col = Color.yellow;
       break;
     }
-  
+
     return col;
   }
+
+  /**
+   * Generates a colour that is interpolated between
+   * <code>colour.darker()</code> and <code>colour.brighter()</code> in
+   * proportion as <code>value</code> is between <code>min</code> and
+   * <code>max</code>. Note that the 'neutral point' (unchanged colour) is
+   * closer to 'brighter' than to 'darker'as this is a geometric range.
+   * 
+   * @param value
+   * @param min
+   * @param max
+   * @param colour
+   * @return
+   */
+  public static Color getGraduatedColour(float value, float min, float max,
+          Color colour)
+  {
+    /*
+     * this computes the equivalent of
+     * getGraduatedColour(value, min, colour.darker(), max, colour.brighter())
+     * but avoiding object creation except for the return value
+     */
+    if (value < min)
+    {
+      value = min;
+    }
+    if (value > max)
+    {
+      value = max;
+    }
+
+    int r = colour.getRed();
+    int g = colour.getGreen();
+    int b = colour.getBlue();
+
+    /*
+     * rgb for colour.darker():
+     */
+    float minR = r * FACTOR;
+    float minG = g * FACTOR;
+    float minB = b * FACTOR;
+
+    /*
+     * rgb for colour.brighter():
+     */
+    float maxR = Math.min(255f, r / FACTOR);
+    float maxG = Math.min(255f, g / FACTOR);
+    float maxB = Math.min(255f, b / FACTOR);
+
+    /*
+     * interpolation
+     */
+    float p = (value - min) / (max - min);
+    int newR = (int) (minR + p * (maxR - minR));
+    int newG = (int) (minG + p * (maxG - minG));
+    int newB = (int) (minB + p * (maxB - minB));
+
+    return new Color(newR, newG, newB, colour.getAlpha());
+  }
 }