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}