001    package ui.recognizer;
002    
003    import java.awt.Color;
004    //import java.awt.color.ColorSpace;
005    
006    /** Represents the color of visible light for a given wavelenth between
007     *  between 380 nm and 780 nm. Originally from a program by Dan Bruton at
008     *  http://www.physics.sfasu.edu/astro/color/spectra.html
009     *
010     * @author John Talbot
011     */
012    public class MonochromaticColor extends Color {
013    
014        public static final double MINIMUM_WAVELENGTH = 380.0 * Units.NANOMETERS;
015        public static final double WAVELENGTH_BLUE    = 440.0 * Units.NANOMETERS;
016        public static final double WAVELENGTH_CYAN    = 490.0 * Units.NANOMETERS;
017        public static final double WAVELENGTH_GREEN   = 510.0 * Units.NANOMETERS;
018        public static final double WAVELENGTH_YELLOW  = 580.0 * Units.NANOMETERS;
019        public static final double WAVELENGTH_RED     = 645.0 * Units.NANOMETERS;
020        public static final double MAXIMUM_WAVELENGTH = 780.0 * Units.NANOMETERS;
021    
022        public static final double BLUE_SENSITIVITY_DROP = 420.0 * Units.NANOMETERS;
023        public static final double RED_SENSITIVITY_DROP  = 700.0 * Units.NANOMETERS;
024    
025        public static final double TYPICAL_GAMMA = 0.80d;
026        public static final double MAXIMUM_INTENSITY = 1.0d;
027    
028        private double wavelength;
029        private double gamma = TYPICAL_GAMMA;
030    
031        /** Construct a monochromatic color based on a wavelength between 380 nm and 780 nm
032         * @param aWavelength the wavelength of the visible radiation in meters
033         */
034        public MonochromaticColor(double aWavelength) {
035            this(aWavelength, MAXIMUM_INTENSITY);
036        }
037    
038        /** Construct a monochromatic color based on a wavelength between 380 nm and 780 nm and enhance the contrast using a gamma value.
039         * @param aWavelength the wavelength of the visible radiation in meters
040         * @param intensity a number between 0 and 1.0 (MAXIMUM_INTENSITY) which is to multiply the color
041         */
042        public MonochromaticColor(double aWavelength, double intensity) {
043            this(aWavelength, intensity, TYPICAL_GAMMA);
044        }
045    
046        /** Construct a monochromatic color based on a wavelength between 380 nm and 780 nm and enhance the contrast using a gamma value.
047         * @param aWavelength the wavelength of the visible radiation in meters
048         * @param intensity a number between 0 and 1.0 (MAXIMUM_INTENSITY) which is to multiply the color
049         * @param aGamma a number which can be used to enhance the contrast of darker colors
050         */
051        public MonochromaticColor(double aWavelength, double intensity, double aGamma) {
052            // the colorspace method of constructing colors is very noisy and non-linear, it was abandonned
053            //super(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), getColorComponents(aWavelength, intensity, aGamma), 1.0f);
054            //super(ColorSpace.getInstance(ColorSpace.CS_sRGB), getColorComponents(aWavelength, intensity, aGamma), 1.0f);
055    
056            // TODO: improve efficiency by reducing this line to one call to getColorComponents()
057            super(getColorComponents(aWavelength, intensity, aGamma)[0],
058                  getColorComponents(aWavelength, intensity, aGamma)[1],
059                  getColorComponents(aWavelength, intensity, aGamma)[2]);
060            setWavelength(aWavelength);
061            setGamma(aGamma);
062        }
063    
064        /** Get a monochromatic color based on a wavelength between 380 nm and 780 nm
065         * and scale the brightness by an intensity parameter between zero and one.
066         * The contrast is enhanced using a gamma value between 0.0 and 1.0.
067         * @param aWavelength the wavelength of the visible radiation in meters
068         * @param intensity a number between 0 and 1.0 which is to multiply the color
069         * @param aGamma a number which can be used to enhance the contrast of darker colors
070         * @return a color which represents one wavelength of visible light
071         */
072        public static float[] getColorComponents(double aWavelength, double intensity, double aGamma) {
073            double red;
074            double green;
075            double blue;
076            if (aWavelength >= MINIMUM_WAVELENGTH && aWavelength <= WAVELENGTH_BLUE) {
077                red   = (WAVELENGTH_BLUE - aWavelength) / (WAVELENGTH_BLUE - MINIMUM_WAVELENGTH);
078                green = 0.0d;
079                blue  = 1.0d;
080            } else if (aWavelength >= WAVELENGTH_BLUE && aWavelength <= WAVELENGTH_CYAN) {
081                red   = 0.0d;
082                green = (aWavelength - WAVELENGTH_BLUE) / (WAVELENGTH_CYAN - WAVELENGTH_BLUE);
083                blue  = 1.0d;
084            } else if (aWavelength >= WAVELENGTH_CYAN && aWavelength <= WAVELENGTH_GREEN) {
085                red   = 0.0d;
086                green = 1.0d;
087                blue  = (WAVELENGTH_GREEN - aWavelength) / (WAVELENGTH_GREEN - WAVELENGTH_CYAN);
088            } else if (aWavelength >= WAVELENGTH_GREEN && aWavelength <= WAVELENGTH_YELLOW) {
089                red   = (aWavelength - WAVELENGTH_GREEN) / (WAVELENGTH_YELLOW - WAVELENGTH_GREEN);
090                green = 1.0d;
091                blue  = 0.0d;
092            } else if (aWavelength >= WAVELENGTH_YELLOW && aWavelength <= WAVELENGTH_RED) {
093                red   = 1.0d;
094                green = (WAVELENGTH_RED - aWavelength) / (WAVELENGTH_RED - WAVELENGTH_YELLOW);
095                blue  = 0.0d;
096            } else if (aWavelength >= WAVELENGTH_RED && aWavelength <= MAXIMUM_WAVELENGTH) {
097                red   = 1.0d;
098                green = 0.0d;
099                blue  = 0.0d;
100            } else {
101                red   = 0.0d;
102                green = 0.0d;
103                blue  = 0.0d;
104            }
105            double spectralSensitivity;
106    
107            if (aWavelength  > RED_SENSITIVITY_DROP) {
108                spectralSensitivity = 0.3d + 0.7d * (MAXIMUM_WAVELENGTH - aWavelength) / (MAXIMUM_WAVELENGTH - RED_SENSITIVITY_DROP);
109            } else if (aWavelength < BLUE_SENSITIVITY_DROP) {
110                spectralSensitivity = 0.3d + 0.7d * (aWavelength - MINIMUM_WAVELENGTH) / (BLUE_SENSITIVITY_DROP - MINIMUM_WAVELENGTH);
111            } else {
112                spectralSensitivity = 1.0d;
113            }
114    
115            // adjust gamma
116            red   = Math.pow(spectralSensitivity * red,   aGamma);
117            green = Math.pow(spectralSensitivity * green, aGamma);
118            blue  = Math.pow(spectralSensitivity * blue,  aGamma);
119    
120            float[] colors = new float[] { (float) (intensity * red), (float) (intensity * green), (float) (intensity * blue) };
121            return colors;
122        }
123    
124    
125    //  return new Color((float) (intensity * red), (float) (intensity * green), (float) (intensity * blue));
126    
127    
128        public static void main(String[] args) {
129            double wavelength, min = 380e-9, max = 780e-9, fraction;
130            int maxpoints = 512;
131            for (int i = 0; i < maxpoints; i++) {
132                fraction = ((double) i) / ((double) maxpoints);
133                wavelength = min + fraction * (max - min);
134                Color aColor = new MonochromaticColor(wavelength);
135                System.out.println(wavelength + " " + aColor.getRed() + " " + aColor.getGreen() + "  " + aColor.getBlue() );
136            }
137        }
138    
139        //--------------------------accessor methods--------------------------------------------
140    
141        /** Get the wavelength in meters which this color represents.
142         * @return a floating point number representing the wavelength in meters
143         */
144        public double getWavelength() {
145            return wavelength;
146        }
147    
148        /** Set the wavelength in meters, and change this color to the closest color match.
149         * @param aWavelength a floating point number representing the wavelength in meters
150         */
151        public void setWavelength(double aWavelength) {
152            wavelength = aWavelength;
153            // TODO: change this color to suit the wavelength
154        }
155    
156        /** Set the wavelength in nanometers and change this color to the closest color match.
157         * @param aWavelengthInNanoMeters a floating point number representing the wavelength in meters
158         */
159        public void setWavelengthInNanoMeters(double aWavelengthInNanoMeters) {
160            setWavelength(aWavelengthInNanoMeters * Units.NANOMETERS);
161        }
162    
163        /** Set the wavelength in angstrom units and change this color to the closest color match.
164         * @param aWavelengthInAngtroms a floating point number representing the wavelength in meters
165         */
166        public void setWavelengthInAngtroms(double aWavelengthInAngtroms) {
167            setWavelength(aWavelengthInAngtroms * Units.ANGSTROMS);
168        }
169    
170        /** Set the gamma, a number which can be used to enhance the contrast of darker colors.
171         * @param aGamma a number representing the gamma of the color
172         */
173        public void setGamma(double aGamma) {
174            gamma = aGamma;
175        }
176    
177        /** Get the gamma, a number which can be used to enhance the contrast of darker colors.
178         * @return a number representing the gamma of the color
179         */
180        public double getGamma() {
181            return gamma;
182        }
183    }