src/core/net/sf/basedb/util/ColorGenerator.java

Code
Comments
Other
Rev Date Author Line
2733 16 Oct 06 nicklas 1 /**
2733 16 Oct 06 nicklas 2   $Id$
2733 16 Oct 06 nicklas 3
3675 16 Aug 07 jari 4   Copyright (C) 2006 Nicklas Nordborg
2733 16 Oct 06 nicklas 5
2733 16 Oct 06 nicklas 6   This file is part of BASE - BioArray Software Environment.
2733 16 Oct 06 nicklas 7   Available at http://base.thep.lu.se/
2733 16 Oct 06 nicklas 8
2733 16 Oct 06 nicklas 9   BASE is free software; you can redistribute it and/or
2733 16 Oct 06 nicklas 10   modify it under the terms of the GNU General Public License
4479 05 Sep 08 jari 11   as published by the Free Software Foundation; either version 3
2733 16 Oct 06 nicklas 12   of the License, or (at your option) any later version.
2733 16 Oct 06 nicklas 13
2733 16 Oct 06 nicklas 14   BASE is distributed in the hope that it will be useful,
2733 16 Oct 06 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
2733 16 Oct 06 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2733 16 Oct 06 nicklas 17   GNU General Public License for more details.
2733 16 Oct 06 nicklas 18
2733 16 Oct 06 nicklas 19   You should have received a copy of the GNU General Public License
4515 11 Sep 08 jari 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
2733 16 Oct 06 nicklas 21 */
2733 16 Oct 06 nicklas 22 package net.sf.basedb.util;
2733 16 Oct 06 nicklas 23
2733 16 Oct 06 nicklas 24 import java.awt.Color;
2733 16 Oct 06 nicklas 25
2733 16 Oct 06 nicklas 26 import net.sf.basedb.core.InvalidDataException;
2733 16 Oct 06 nicklas 27 import net.sf.basedb.core.NumberOutOfRangeException;
2733 16 Oct 06 nicklas 28
2733 16 Oct 06 nicklas 29 /**
2733 16 Oct 06 nicklas 30   This class is used to generate colors for numerical values. Given a
2733 16 Oct 06 nicklas 31   numeric range mith a start, mid and max value and one color for each
2733 16 Oct 06 nicklas 32   of these values you can create new colors for values within the specified
2733 16 Oct 06 nicklas 33   range.
2733 16 Oct 06 nicklas 34   <p>
2733 16 Oct 06 nicklas 35   Example:<br>
2733 16 Oct 06 nicklas 36   We have the range and colors: <code>-1 (BLUE), 0 (WHITE) , +1 (YELLOW)</code>
2733 16 Oct 06 nicklas 37   The value -0.5 is converted to a lighter blue and the value +0.5 is converted
2733 16 Oct 06 nicklas 38   to a ligther yellow. Values falling outside the min or max values are
2733 16 Oct 06 nicklas 39   assigned the endpoint colors. In this case, BLUE and YELLOW.
2733 16 Oct 06 nicklas 40
2733 16 Oct 06 nicklas 41   @author nicklas
2733 16 Oct 06 nicklas 42   @version 2.0
2733 16 Oct 06 nicklas 43   @base.modified $Date$
2733 16 Oct 06 nicklas 44 */
2733 16 Oct 06 nicklas 45 public class ColorGenerator
2733 16 Oct 06 nicklas 46 {
2733 16 Oct 06 nicklas 47
2733 16 Oct 06 nicklas 48   /**
2733 16 Oct 06 nicklas 49     Constant holding the value of <code>1 / ln(2)</code>. It is used
2733 16 Oct 06 nicklas 50     to calculate the two-based logarithm of values using the formula:
2733 16 Oct 06 nicklas 51     <code>log2(x) = ln(x) / ln(2)</code>
2733 16 Oct 06 nicklas 52   */
2733 16 Oct 06 nicklas 53   public static final float INVLN2 = (float)(1 / Math.log(2));
2733 16 Oct 06 nicklas 54   
2733 16 Oct 06 nicklas 55   /**
2733 16 Oct 06 nicklas 56     Convert a hexadecimal color string such as one used in HTML
2733 16 Oct 06 nicklas 57     to a Color object. The hexadecimal string must have 6 characters
2733 16 Oct 06 nicklas 58     in the range 0-9 or A-F (case ignored).
2733 16 Oct 06 nicklas 59     @param hexRGB The hexadecimal color string
2733 16 Oct 06 nicklas 60     @return A Color object
2733 16 Oct 06 nicklas 61     @throws InvalidDataException If the string cannot be converted
2733 16 Oct 06 nicklas 62   */
2733 16 Oct 06 nicklas 63   public static Color fromHex(String hexRGB)
2733 16 Oct 06 nicklas 64   {
2733 16 Oct 06 nicklas 65     if (hexRGB == null || !hexRGB.matches("[0-9A-Fa-f]{6}"))
2733 16 Oct 06 nicklas 66     {
2733 16 Oct 06 nicklas 67       throw new InvalidDataException("Not a hex RGB value: " + hexRGB);
2733 16 Oct 06 nicklas 68     }
2733 16 Oct 06 nicklas 69     int r = Integer.parseInt(hexRGB.substring(0, 2), 16);
2733 16 Oct 06 nicklas 70     int g = Integer.parseInt(hexRGB.substring(2, 4), 16);
2733 16 Oct 06 nicklas 71     int b = Integer.parseInt(hexRGB.substring(4, 6), 16);
2733 16 Oct 06 nicklas 72     return new Color(r, g, b);
2733 16 Oct 06 nicklas 73   }
2733 16 Oct 06 nicklas 74
2733 16 Oct 06 nicklas 75   /**
2733 16 Oct 06 nicklas 76     Generate a hex color string from a Color object. Useful for
2733 16 Oct 06 nicklas 77     outputting into HTML pages.
2733 16 Oct 06 nicklas 78     
2733 16 Oct 06 nicklas 79     @param color The color object
2733 16 Oct 06 nicklas 80     @return The hexadecimal representation of the color
2733 16 Oct 06 nicklas 81   */
2733 16 Oct 06 nicklas 82   public static String toHex(Color color)
2733 16 Oct 06 nicklas 83   {
2733 16 Oct 06 nicklas 84     StringBuilder sb = new StringBuilder(6);
2733 16 Oct 06 nicklas 85     int r = color.getRed();
2733 16 Oct 06 nicklas 86     if (r < 16) sb.append("0");
2733 16 Oct 06 nicklas 87     sb.append(Integer.toHexString(r));
2733 16 Oct 06 nicklas 88
2733 16 Oct 06 nicklas 89     int g = color.getGreen();
2733 16 Oct 06 nicklas 90     if (g < 16) sb.append("0");
2733 16 Oct 06 nicklas 91     sb.append(Integer.toHexString(g));
2733 16 Oct 06 nicklas 92   
2733 16 Oct 06 nicklas 93     int b = color.getBlue();
2733 16 Oct 06 nicklas 94     if (b < 16) sb.append("0");
2733 16 Oct 06 nicklas 95     sb.append(Integer.toHexString(b));
2733 16 Oct 06 nicklas 96     return sb.toString();
2733 16 Oct 06 nicklas 97   }
2733 16 Oct 06 nicklas 98   
2733 16 Oct 06 nicklas 99   private Color minColor;
2733 16 Oct 06 nicklas 100   private Color midColor;
2733 16 Oct 06 nicklas 101   private Color maxColor;
2733 16 Oct 06 nicklas 102   private float minValue;
2733 16 Oct 06 nicklas 103   private float midValue;
2733 16 Oct 06 nicklas 104   private float maxValue;
2733 16 Oct 06 nicklas 105   private boolean log;
2733 16 Oct 06 nicklas 106   
2733 16 Oct 06 nicklas 107   /**
2733 16 Oct 06 nicklas 108     The RGB components of the min color. [0] = red; [1] = green; [2] = blue
2733 16 Oct 06 nicklas 109   */
2733 16 Oct 06 nicklas 110   private final int[] offsetMin;
2733 16 Oct 06 nicklas 111
2733 16 Oct 06 nicklas 112   /**
2733 16 Oct 06 nicklas 113     The RGB components of the midpoint color. [0] = red; [1] = green; [2] = blue
2733 16 Oct 06 nicklas 114   */
2733 16 Oct 06 nicklas 115   private final int[] offsetMid;
2733 16 Oct 06 nicklas 116   
2733 16 Oct 06 nicklas 117   /**
2733 16 Oct 06 nicklas 118     The difference of the RGB components of the min and mid
2733 16 Oct 06 nicklas 119     colors. [0] = red; [1] = green; [2] = blue
2733 16 Oct 06 nicklas 120   */
2733 16 Oct 06 nicklas 121   private final int[] deltaMinMid;
2733 16 Oct 06 nicklas 122
2733 16 Oct 06 nicklas 123   /**
2733 16 Oct 06 nicklas 124     The difference of the RGB components of the mid and max
2733 16 Oct 06 nicklas 125     colors. [0] = red; [1] = green; [2] = blue
2733 16 Oct 06 nicklas 126   */
2733 16 Oct 06 nicklas 127   private final int[] deltaMidMax;
2733 16 Oct 06 nicklas 128   
2733 16 Oct 06 nicklas 129   /**
2733 16 Oct 06 nicklas 130     The inverted difference between the mid and min values:
2733 16 Oct 06 nicklas 131     1 / (midValue - minValue)
2733 16 Oct 06 nicklas 132   */
2733 16 Oct 06 nicklas 133   private final float rangeMinMid;
2733 16 Oct 06 nicklas 134
2733 16 Oct 06 nicklas 135   /**
2733 16 Oct 06 nicklas 136     The inverted difference between the max and mid values:
2733 16 Oct 06 nicklas 137     1 / (maxValue - midValue)
2733 16 Oct 06 nicklas 138   */
2733 16 Oct 06 nicklas 139   private final float rangeMidMax;
2733 16 Oct 06 nicklas 140   
2733 16 Oct 06 nicklas 141   /**
2733 16 Oct 06 nicklas 142     Create a new color generator.
2733 16 Oct 06 nicklas 143     @param minColor The color for the minimum endpoint
2733 16 Oct 06 nicklas 144     @param midColor The color for the midpoint
2733 16 Oct 06 nicklas 145     @param maxColor The color for the maximum endpoint
2733 16 Oct 06 nicklas 146     @param minValue The value for the minimum endpoint
2733 16 Oct 06 nicklas 147     @param midValue The value for the midpoint
2733 16 Oct 06 nicklas 148     @param maxValue The value for the maximum endpoint
2733 16 Oct 06 nicklas 149     @param log If values should be logarithmised before beeing assigned a 
2733 16 Oct 06 nicklas 150       color or not. Note the values in this constructor are not affected by this
2733 16 Oct 06 nicklas 151       setting.
2733 16 Oct 06 nicklas 152     @throws InvalidDataException If not the minValue &lt;= midValue &lt;= maxValue 
2733 16 Oct 06 nicklas 153   */
2733 16 Oct 06 nicklas 154   public ColorGenerator(Color minColor, Color midColor, Color maxColor, 
2733 16 Oct 06 nicklas 155     float minValue, float midValue, float maxValue, boolean log)
2733 16 Oct 06 nicklas 156   {
2733 16 Oct 06 nicklas 157     // Check that minValue <= midValue <= maxValue
2733 16 Oct 06 nicklas 158     if (minValue > midValue)
2733 16 Oct 06 nicklas 159     {
2733 16 Oct 06 nicklas 160       throw new NumberOutOfRangeException("minValue", minValue, midValue, true);
2733 16 Oct 06 nicklas 161     }
2733 16 Oct 06 nicklas 162     if (midValue > maxValue)
2733 16 Oct 06 nicklas 163     {
2733 16 Oct 06 nicklas 164       throw new NumberOutOfRangeException("midValue", midValue, maxValue, true);      
2733 16 Oct 06 nicklas 165     }
2733 16 Oct 06 nicklas 166     
2733 16 Oct 06 nicklas 167     // Store paramters
2733 16 Oct 06 nicklas 168     this.minColor = minColor;
2733 16 Oct 06 nicklas 169     this.midColor = midColor;
2733 16 Oct 06 nicklas 170     this.maxColor = maxColor;
2733 16 Oct 06 nicklas 171     this.minValue = minValue;
2733 16 Oct 06 nicklas 172     this.midValue = midValue;
2733 16 Oct 06 nicklas 173     this.maxValue = maxValue;
2733 16 Oct 06 nicklas 174     this.log = log;
2733 16 Oct 06 nicklas 175     
2733 16 Oct 06 nicklas 176     // Precalculate some settings
2733 16 Oct 06 nicklas 177     this.offsetMin = new int[3];
2733 16 Oct 06 nicklas 178     offsetMin[0] = minColor.getRed();
2733 16 Oct 06 nicklas 179     offsetMin[1] = minColor.getGreen();
2733 16 Oct 06 nicklas 180     offsetMin[2] = minColor.getBlue();
2733 16 Oct 06 nicklas 181
2733 16 Oct 06 nicklas 182     this.offsetMid = new int[3];
2733 16 Oct 06 nicklas 183     offsetMid[0] = midColor.getRed();
2733 16 Oct 06 nicklas 184     offsetMid[1] = midColor.getGreen();
2733 16 Oct 06 nicklas 185     offsetMid[2] = midColor.getBlue();
2733 16 Oct 06 nicklas 186     
2733 16 Oct 06 nicklas 187     this.deltaMinMid = new int[3];
2733 16 Oct 06 nicklas 188     for (int i = 0; i < 3; ++i)
2733 16 Oct 06 nicklas 189     {
2733 16 Oct 06 nicklas 190       deltaMinMid[i] = offsetMid[i] - offsetMin[i];
2733 16 Oct 06 nicklas 191     }
2733 16 Oct 06 nicklas 192     
2733 16 Oct 06 nicklas 193     this.deltaMidMax = new int[3];
2733 16 Oct 06 nicklas 194     deltaMidMax[0] = maxColor.getRed() - offsetMid[0];
2733 16 Oct 06 nicklas 195     deltaMidMax[1] = maxColor.getGreen() - offsetMid[1];
2733 16 Oct 06 nicklas 196     deltaMidMax[2] = maxColor.getBlue() - offsetMid[2];
2733 16 Oct 06 nicklas 197     
2733 16 Oct 06 nicklas 198     this.rangeMinMid = midValue == minValue ? 0 : 1 / (midValue - minValue);
2733 16 Oct 06 nicklas 199     this.rangeMidMax = maxValue == midValue ? 0 : 1 / (maxValue - midValue);
2733 16 Oct 06 nicklas 200   }
2733 16 Oct 06 nicklas 201   
2733 16 Oct 06 nicklas 202   /**
2733 16 Oct 06 nicklas 203     Get the color for the value. If the log flag was true when creating the
2733 16 Oct 06 nicklas 204     generator the value is first logarithmised. Note that negative values
2733 16 Oct 06 nicklas 205     can't be logarithmised so null is returned in those cases.
2733 16 Oct 06 nicklas 206     <p>
2733 16 Oct 06 nicklas 207     If the value is lower than the min endpoint the min endpoint color
2733 16 Oct 06 nicklas 208     is returned. If the value is higher than the max endpoint the
2733 16 Oct 06 nicklas 209     max endpoint color is returned. Otherwise a new color is calculated
2733 16 Oct 06 nicklas 210     based on the difference between the value and the given points.
2733 16 Oct 06 nicklas 211     
2733 16 Oct 06 nicklas 212     @param value The value
2733 16 Oct 06 nicklas 213     @return A color object or null if no color could be generated
2733 16 Oct 06 nicklas 214   */
2733 16 Oct 06 nicklas 215   public Color getColor(float value)
2733 16 Oct 06 nicklas 216   {
2789 24 Oct 06 nicklas 217     if (Float.isNaN(value)) return null;
2733 16 Oct 06 nicklas 218     
2733 16 Oct 06 nicklas 219     if (log)
2733 16 Oct 06 nicklas 220     {
2733 16 Oct 06 nicklas 221       if (value <= 0) return null;
2733 16 Oct 06 nicklas 222       value = ((float)Math.log(value) * INVLN2);
2733 16 Oct 06 nicklas 223     }
2733 16 Oct 06 nicklas 224     
2733 16 Oct 06 nicklas 225     Color newColor = null;
2733 16 Oct 06 nicklas 226     if (value >= maxValue)
2733 16 Oct 06 nicklas 227     {
2733 16 Oct 06 nicklas 228       newColor = maxColor;
2733 16 Oct 06 nicklas 229     }
2733 16 Oct 06 nicklas 230     else if (value <= minValue)
2733 16 Oct 06 nicklas 231     {
2733 16 Oct 06 nicklas 232       newColor = minColor;
2733 16 Oct 06 nicklas 233     }
2733 16 Oct 06 nicklas 234     else
2733 16 Oct 06 nicklas 235     {
2733 16 Oct 06 nicklas 236       float factor;
2733 16 Oct 06 nicklas 237       int[] delta;
2733 16 Oct 06 nicklas 238       int[] offset;
2733 16 Oct 06 nicklas 239       if (value >= midValue)
2733 16 Oct 06 nicklas 240       {
2733 16 Oct 06 nicklas 241         delta = deltaMidMax;
2733 16 Oct 06 nicklas 242         offset = offsetMid;
2733 16 Oct 06 nicklas 243         factor = (value - midValue) * rangeMidMax;
2733 16 Oct 06 nicklas 244       }
2733 16 Oct 06 nicklas 245       else
2733 16 Oct 06 nicklas 246       {
2733 16 Oct 06 nicklas 247         delta = deltaMinMid;
2733 16 Oct 06 nicklas 248         offset = offsetMin;
2733 16 Oct 06 nicklas 249         factor = (value - minValue) * rangeMinMid;
2733 16 Oct 06 nicklas 250       }
2733 16 Oct 06 nicklas 251       int r = (int)(offset[0] + factor * delta[0]);
2733 16 Oct 06 nicklas 252       int g = (int)(offset[1] + factor * delta[1]);
2733 16 Oct 06 nicklas 253       int b = (int)(offset[2] + factor * delta[2]);
2733 16 Oct 06 nicklas 254       newColor = new Color(r, g, b);
2733 16 Oct 06 nicklas 255     }
2733 16 Oct 06 nicklas 256     return newColor;
2733 16 Oct 06 nicklas 257   }
2733 16 Oct 06 nicklas 258   
2733 16 Oct 06 nicklas 259 }