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 java.awt.*; 025import java.awt.color.ColorSpace; 026 027/** 028 * @author Spencer Bliven 029 * 030 */ 031public class LinearColorInterpolator implements ColorInterpolator { 032 033 public enum InterpolationDirection { 034 INNER, /* within [a,b] */ 035 OUTER, /* outside ]a,b[ */ 036 UPPER, /* above a, eg in [a, a+(b-a)%1] */ 037 LOWER /* below a */ 038 } 039 040 private ColorSpace colorSpace; 041 private InterpolationDirection[] interpolationDirection; 042 043 public LinearColorInterpolator() { 044 this(ColorSpace.getInstance(ColorSpace.CS_sRGB)); 045 } 046 public LinearColorInterpolator(ColorSpace colorSpace) { 047 super(); 048 this.setColorSpace(colorSpace); 049 } 050 051 /** 052 * Interpolates to a color between a and b 053 * @param a First color 054 * @param b Second color 055 * @param mixing Mixing coefficient; the fraction of a in the result. 056 * @return The color between a and b 057 * @throws IllegalArgumentException if mixing is not between 0 and 1 058 * @see org.biojava.nbio.structure.gui.util.color.ColorInterpolator#interpolate(java.awt.Color, java.awt.Color, float) 059 */ 060 @Override 061 public Color interpolate(Color a, Color b, float mixing) { 062 float[] compA, compB; 063 // Get components 064 // Don't convert colorSpaces unless necessary 065 if(a.getColorSpace().equals(colorSpace) ) { 066 compA = a.getComponents(null); 067 } else { 068 compA = a.getComponents(colorSpace, null); 069 } 070 if(b.getColorSpace().equals(colorSpace)) { 071 compB = b.getComponents(null); 072 } else { 073 compB = b.getComponents(colorSpace, null); 074 } 075 076 float[] compMixed = new float[compA.length]; 077 078 for(int i=0;i<compA.length;i++){ 079 //Normalizing to [0,1] after the interpolation, 080 // INNER means between a and b 081 // OUTER means between max(a,b) and min(a,b)+1 082 // UPPER means between a and b' s.t. b'>a and b' in {b, b+1} 083 // LOWER means between a and b' s.t. b'<a and b' in {b, b-1} 084 float left, right; 085 left = compA[i]; 086 //Alpha uses INNER direction 087 InterpolationDirection dir = i<interpolationDirection.length ? 088 interpolationDirection[i] : InterpolationDirection.INNER; 089 switch(dir) { 090 case INNER: 091 right = compB[i]; 092 break; 093 case OUTER: 094 if(compA[i]<compB[i]) { 095 right = compB[i]-1; 096 } else { 097 right = compB[i]+1; 098 } 099 break; 100 case UPPER: 101 if(compA[i]<compB[i]) { 102 right = compB[i]; 103 } else { 104 right = compB[i]+1; 105 } 106 break; 107 case LOWER: 108 if(compA[i]<compB[i]) { 109 right = compB[i]-1; 110 } else { 111 right = compB[i]; 112 } 113 break; 114 default: throw new IllegalStateException("Unkown interpolation Direction "+interpolationDirection[i]); 115 } 116 117 //Perform mixing 118 compMixed[i] = mixing*left + (1-mixing)*right; 119 120 if(dir != InterpolationDirection.INNER) { 121 //Normalize to [0,1] 122 if(compMixed[i] < 0) 123 compMixed[i] += 1f; 124 if(compMixed[i] > 1) 125 compMixed[i] -= 1f; 126 } 127 } 128 129 return new Color(colorSpace,compMixed,compMixed[compMixed.length-1]); 130 } 131 132 133 /** 134 * Sets the ColorSpace to use for interpolation. 135 * 136 * The most common scheme for color spaces is to use linear components 137 * between 0 and 1 (for instance red,green,blue). For such a component, a 138 * linear interpolation between two colors is used. 139 * 140 * Sometimes a component may be in cylindrical coordinates. In this case, 141 * the component can be mapped in a number of ways. These are set by 142 * InterpolationDirections. 143 * 144 * @param colorSpace The color space for interpolation 145 * @param interpDirection An array of size colorSpace.getNumComponents() 146 * giving the interpolation direction for each component. 147 */ 148 public void setColorSpace(ColorSpace colorSpace, InterpolationDirection[] dir) { 149 if(dir.length < colorSpace.getNumComponents()) { 150 throw new IllegalArgumentException( "Must specify an interpolation " + 151 "direction for each colorspace component ("+colorSpace.getNumComponents()+")"); 152 } 153 this.colorSpace = colorSpace; 154 this.interpolationDirection = dir; 155 } 156 157 public void setColorSpace(ColorSpace colorSpace) { 158 InterpolationDirection[] dir = new InterpolationDirection[colorSpace.getNumComponents()]; 159 for(int i=0;i<dir.length;i++) 160 dir[i] = InterpolationDirection.INNER; 161 this.setColorSpace(colorSpace, dir); 162 } 163 164 public void setInterpolationDirection(int componentIndex, InterpolationDirection dir) { 165 interpolationDirection[componentIndex] = dir; 166 } 167 168 169}