001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 * Created on Aug 3, 2007 021 */ 022package org.biojava.nbio.structure.gui.util.color; 023 024import org.biojava.nbio.structure.gui.util.color.LinearColorInterpolator.InterpolationDirection; 025 026import javax.swing.*; 027import java.awt.*; 028import java.awt.color.ColorSpace; 029import java.util.*; 030 031/** 032 * Maps a set of real values onto a gradient. 033 * 034 * The real line is partitioned into segments [a,b). The endpoint of each segment is labeled with a color. 035 * Colors are linearly interpolated between finite endpoints. Endpoints implicitly exist for 036 * Double.NEGATIVE_INFINITY and Double.POSITIVE_INFINITY, representing default colors. Thus any point 037 * in the segment [-Inf,a) is labeled with the negInf color, and any point in [b,Inf] is labeled with the posInf 038 * color. If no endpoints are present, the posInf color is used as default. 039 * 040 * Common gradients are predefined an may be instantiated through 041 * GradientMapper.getGradientMapper(). 042 * 043 * @author Spencer Bliven 044 * 045 */ 046public class GradientMapper implements ContinuousColorMapper, Map<Double, Color> { 047 048 public static final int BLACK_WHITE_GRADIENT = 1; 049 public static final int WHITE_BLACK_GRADIENT = 2; 050 public static final int RED_BLUE_GRADIENT = 3; 051 public static final int RAINBOW_GRADIENT = 4; 052 public static final int RAINBOW_INTENSITY_GRADIENT = 5; 053 054 private NavigableMap<Double,Color> mapping; 055 private ColorInterpolator interpolator; 056 057 public GradientMapper() { 058 this(Color.black,Color.white); 059 } 060 public GradientMapper(Color negInf, Color posInf) { 061 this(negInf,posInf,ColorSpace.getInstance(ColorSpace.CS_sRGB)); 062 } 063 public GradientMapper(Color negInf, Color posInf, ColorSpace cspace) { 064 mapping = new TreeMap<Double,Color>(); 065 mapping.put(Double.NEGATIVE_INFINITY, negInf); 066 mapping.put(Double.POSITIVE_INFINITY, posInf); 067 interpolator = new LinearColorInterpolator(cspace); 068 } 069 070 /** 071 * Constructs a gradientMapper to draw one of the pre-defined gradients 072 * 073 * For example, 074 * GradientMapper.getGradientMapper(GradientMapper.RAINBOW_GRADIENT, 0, 10) 075 * 076 * @param gradientType One of the gradient types, eg GradientMapper.BLACK_WHITE_GRADIENT 077 * @param min Start of the gradient 078 * @param max End of the gradient 079 * @return 080 */ 081 public static GradientMapper getGradientMapper(int gradientType, double min, double max) { 082 GradientMapper gm; 083 switch( gradientType ) { 084 case BLACK_WHITE_GRADIENT: 085 gm = new GradientMapper(Color.BLACK, Color.WHITE); 086 gm.put(min, Color.BLACK); 087 gm.put(max, Color.WHITE); 088 return gm; 089 case WHITE_BLACK_GRADIENT: 090 gm = new GradientMapper(Color.WHITE, Color.BLACK); 091 gm.put(min, Color.WHITE); 092 gm.put(max, Color.BLACK); 093 return gm; 094 case RED_BLUE_GRADIENT: 095 gm = new GradientMapper(Color.RED, Color.BLUE); 096 gm.put(min, Color.RED); 097 gm.put(max, Color.BLUE); 098 return gm; 099 case RAINBOW_GRADIENT: { 100 //Set up interpolation in HSV colorspace 101 ColorSpace hsv = HSVColorSpace.getHSVColorSpace(); 102 LinearColorInterpolator interp = new LinearColorInterpolator(hsv); 103 interp.setInterpolationDirection(0, InterpolationDirection.UPPER); 104 105 Color hsvLow = new Color(hsv,new float[] {0f, 1f, 1f},1f); 106 Color hsvHigh = new Color(hsv,new float[] {1f, 1f, 1f},1f); 107 108 gm = new GradientMapper(hsvLow, hsvHigh, hsv); 109 gm.put(min, hsvLow); 110 gm.put(max, hsvHigh); 111 gm.setInterpolator(interp); 112 return gm; 113 } 114 case RAINBOW_INTENSITY_GRADIENT: { 115 //Set up interpolation in HSV colorspace 116 ColorSpace hsv = HSVColorSpace.getHSVColorSpace(); 117 LinearColorInterpolator interp = new LinearColorInterpolator(hsv); 118 interp.setInterpolationDirection(0, InterpolationDirection.LOWER); 119 120 Color hsvLow = new Color(hsv,new float[] {1f, 1f, 1f},1f); 121 Color hsvHigh = new Color(hsv,new float[] {0f, 1f, 0f},1f); 122 123 gm = new GradientMapper(hsvLow, hsvHigh, hsv); 124 gm.put(min, hsvLow); 125 gm.put(max, hsvHigh); 126 gm.setInterpolator(interp); 127 return gm; 128 } 129 default: 130 throw new IllegalArgumentException("Unsupported gradient "+gradientType); 131 } 132 } 133 /** 134 * @param value 135 * @return 136 * @see org.biojava.nbio.structure.gui.util.color.ContinuousColorMapper#getColor(double) 137 */ 138 @Override 139 public Color getColor(double value) { 140 Double left = mapping.floorKey(value); 141 Double right = mapping.higherKey(value); 142 143 //don't interpolate to infinity 144 if(right == null || right.isInfinite()) { 145 return mapping.get(Double.POSITIVE_INFINITY); 146 } 147 if(left == null || left.isInfinite()) { 148 return mapping.get(Double.NEGATIVE_INFINITY); 149 } 150 151 // fraction of left color to use 152 float alpha = (float) ((right-value)/(right-left)); 153 return interpolator.interpolate(mapping.get(left),mapping.get(right),alpha); 154 } 155 156 157 158 /*-************************* 159 * Map methods 160 ***************************/ 161 162 163 /** 164 * Clears all finite endpoints 165 * 166 * @see java.util.Map#clear() 167 */ 168 @Override 169 public void clear() { 170 Color neg = mapping.get(Double.NEGATIVE_INFINITY); 171 Color pos = mapping.get(Double.POSITIVE_INFINITY); 172 mapping.clear(); 173 mapping.put(Double.NEGATIVE_INFINITY, neg); 174 mapping.put(Double.POSITIVE_INFINITY, pos); 175 } 176 /** 177 * @param position 178 * @return 179 * @see java.util.Map#containsKey(java.lang.Object) 180 */ 181 @Override 182 public boolean containsKey(Object position) { 183 return mapping.containsKey(position); 184 } 185 /** 186 * @param color 187 * @return 188 * @see java.util.Map#containsValue(java.lang.Object) 189 */ 190 @Override 191 public boolean containsValue(Object color) { 192 return mapping.containsValue(color); 193 } 194 /** 195 * @return 196 * @see java.util.Map#entrySet() 197 */ 198 @Override 199 public Set<java.util.Map.Entry<Double, Color>> entrySet() { 200 return mapping.entrySet(); 201 } 202 /** 203 * @param position 204 * @return The color of the endpoint at position, or null if no endpoint exists there 205 * @see java.util.Map#get(java.lang.Object) 206 */ 207 @Override 208 public Color get(Object position) { 209 return mapping.get(position); 210 } 211 /** 212 * @return true if this gradient does not contain finite endpoints 213 * @see java.util.Map#isEmpty() 214 */ 215 @Override 216 public boolean isEmpty() { 217 return mapping.size() <= 2; 218 } 219 /** 220 * @return 221 * @see java.util.Map#keySet() 222 */ 223 @Override 224 public Set<Double> keySet() { 225 return mapping.keySet(); 226 } 227 /** 228 * Adds a gradient endpoint at the specified position. 229 * @param position The endpoint position. May be Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY for endpoints. 230 * @param color 231 * @return 232 * @see java.util.Map#put(java.lang.Object, java.lang.Object) 233 */ 234 @Override 235 public Color put(Double position, Color color) { 236 if( position == null ) { 237 throw new NullPointerException("Null endpoint position"); 238 } 239 if( color == null ){ 240 throw new NullPointerException("Null colors are not allowed."); 241 } 242 return mapping.put(position, color); 243 } 244 /** 245 * @param m 246 * @see java.util.Map#putAll(java.util.Map) 247 */ 248 @Override 249 public void putAll(Map<? extends Double, ? extends Color> m) { 250 mapping.putAll(m); 251 } 252 /** 253 * @param position 254 * @return 255 * @see java.util.Map#remove(java.lang.Object) 256 */ 257 @Override 258 public Color remove(Object position) { 259 if( ((Double)position).isInfinite() ) { 260 throw new UnsupportedOperationException("Cannot remove infinite endpoints"); 261 } 262 return mapping.remove(position); 263 } 264 /** 265 * @return Number of finite endpoints 266 * @see java.util.Map#size() 267 */ 268 @Override 269 public int size() { 270 return mapping.size()-2; 271 } 272 /** 273 * @return 274 * @see java.util.Map#values() 275 */ 276 @Override 277 public Collection<Color> values() { 278 return mapping.values(); 279 } 280 281 282 /** 283 * @return the interpolator 284 */ 285 public ColorInterpolator getInterpolator() { 286 return interpolator; 287 } 288 /** 289 * @param interpolator the interpolator to set 290 */ 291 public void setInterpolator(ColorInterpolator interpolator) { 292 this.interpolator = interpolator; 293 } 294 /** 295 * @param args 296 */ 297 public static void main(String[] args) { 298 GradientMapper[] mappers = new GradientMapper[20]; 299 int i = 0; 300 ColorSpace hsv = HSVColorSpace.getHSVColorSpace(); 301 LinearColorInterpolator interp; 302 303 304 // RGB colorspace 305 mappers[i] = new GradientMapper(Color.black, Color.white); 306 mappers[i].put(-5., Color.red); 307 mappers[i].put(5., Color.blue); 308 i++; 309 310 // Premade 311 mappers[i] = GradientMapper.getGradientMapper(BLACK_WHITE_GRADIENT,-5,5); 312 i++; 313 mappers[i] = GradientMapper.getGradientMapper(RAINBOW_INTENSITY_GRADIENT,-5,5); 314 //mappers[i].put(Double.NEGATIVE_INFINITY, mappers[i].get(Double.NEGATIVE_INFINITY).brighter()); 315 //mappers[i].put(Double.POSITIVE_INFINITY, mappers[i].get(Double.POSITIVE_INFINITY).darker()); 316 i++; 317 318 // Rainbow 319 mappers[i] = new GradientMapper(Color.black, Color.white, hsv); 320 mappers[i].put(-5., new Color(hsv,new float[] {0f, 1f, 1f},1f)); 321 mappers[i].put( 5., new Color(hsv,new float[] {1f, 1f, 1f},1f)); 322 i++; 323 324 // HSV INNER 325 mappers[i] = new GradientMapper(Color.black, Color.white, hsv); 326 mappers[i].put( 5., Color.red); 327 mappers[i].put(-5., Color.blue); 328 i++; 329 330 // HSV OUTER 331 interp = new LinearColorInterpolator(hsv); 332 interp.setInterpolationDirection(0, InterpolationDirection.OUTER); 333 mappers[i] = new GradientMapper(Color.black, Color.white, hsv); 334 mappers[i].put( 5., Color.red); 335 mappers[i].put(-5., Color.blue); 336 mappers[i].setInterpolator(interp); 337 i++; 338 339 // HSV UPPER 340 interp = new LinearColorInterpolator(hsv); 341 interp.setInterpolationDirection(0, InterpolationDirection.UPPER); 342 mappers[i] = new GradientMapper(Color.black, Color.white, hsv); 343 mappers[i].put( 5., Color.red); 344 mappers[i].put(-5., Color.blue); 345 mappers[i].setInterpolator(interp); 346 i++; 347 348 // HSV LOWER 349 interp = new LinearColorInterpolator(hsv); 350 interp.setInterpolationDirection(0, InterpolationDirection.LOWER); 351 mappers[i] = new GradientMapper(Color.black, Color.white, hsv); 352 mappers[i].put( 5., Color.red); 353 mappers[i].put(-5., Color.blue); 354 mappers[i].setInterpolator(interp); 355 i++; 356 357 // Mimic DefaultMapper 358 interp = new LinearColorInterpolator(hsv); 359 interp.setInterpolationDirection(0, InterpolationDirection.INNER); 360 mappers[i] = new GradientMapper(Color.green, Color.black, hsv); 361 mappers[i].put( 0., new Color(hsv,new float[] {1f, .9f, 1f},1f)); 362 mappers[i].put(10., new Color(hsv,new float[] {0f, .9f, 0f},1f)); 363 mappers[i].setInterpolator(interp); 364 i++; 365 366 // Better DefaultGradient 367 interp = new LinearColorInterpolator(hsv); 368 interp.setInterpolationDirection(0, InterpolationDirection.INNER); 369 mappers[i] = new GradientMapper(Color.green, Color.black, hsv); 370 mappers[i].put( 0., new Color(hsv,new float[] {1f, .9f, 1f},1f)); 371 mappers[i].put( 1., new Color(hsv,new float[] {0f, .9f, 1f},1f)); 372 mappers[i].put( 1+1e-6, Color.white); 373 mappers[i].put(10., Color.black); 374 mappers[i].setInterpolator(interp); 375 i++; 376 // Better DefaultGradient 377 interp = new LinearColorInterpolator(hsv); 378 interp.setInterpolationDirection(0, InterpolationDirection.INNER); 379 mappers[i] = new GradientMapper(Color.green, Color.black, hsv); 380 mappers[i].put( 0., new Color(hsv,new float[] {1f, .9f, 1f},1f)); 381 mappers[i].put( 1., new Color(hsv,new float[] {.2f, .9f, 1f},1f)); 382 mappers[i].put( 1+1e-6, Color.white); 383 mappers[i].put(10., Color.black); 384 mappers[i].setInterpolator(interp); 385 i++; 386 387 388 DefaultMatrixMapper defaultMapper = new DefaultMatrixMapper(10f,.9f); 389 390 391 392 JFrame frame = new JFrame("GradientMapper"); 393 JPanel main = new JPanel(); 394 main.setPreferredSize(new Dimension(300,500)); 395 396 for(int j=0;j<i;j++) { 397 GradientPanel grad1 = new GradientPanel(mappers[j],-10,10); 398 //grad1.setPreferredSize(new Dimension(500,50)); 399 main.add(grad1); 400 } 401 GradientPanel grad2 = new GradientPanel(defaultMapper,-10,10); 402 //grad2.setPreferredSize(new Dimension(500,50)); 403 main.add(grad2); 404 //main.add(new GradientPanel(defaultMapper,-10,10)); 405 406 407 frame.getContentPane().add(main); 408 frame.pack(); 409 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 410 frame.setVisible(true); 411 412 } 413 414 415}