View Javadoc
1   /*
2   
3       dsh-color-scheme  Color schemes.
4       Copyright (c) 2009-2016 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package org.dishevelled.color.scheme.impl;
25  
26  import java.awt.Color;
27  
28  import java.util.ArrayList;
29  import java.util.List;
30  
31  import org.dishevelled.color.scheme.ColorFactory;
32  import org.dishevelled.color.scheme.ColorScheme;
33  
34  /**
35   * Continuous color scheme.
36   *
37   * @author  Michael Heuer
38   */
39  public final class ContinuousColorScheme
40      implements ColorScheme
41  {
42      /** Name. */
43      private final String name;
44  
45      /** List of colors. */
46      private final List<Color> colors;
47  
48      /** List of anchors. */
49      private final List<Double> anchors;
50  
51      /** Minimum value. */
52      private final double minimumValue;
53  
54      /** Maximum value. */
55      private final double maximumValue;
56  
57      /** Color factory. */
58      private final ColorFactory colorFactory;
59  
60  
61      /**
62       * Create a new continuous color scheme.
63       *
64       * @param name name
65       * @param colors list of colors, must not be null and must contain at least two colors
66       * @param minimumValue minimum value
67       * @param maximumValue maximum value
68       * @param colorFactory color factory, must not be null
69       */
70      public ContinuousColorScheme(final String name,
71                                   final List<Color> colors,
72                                   final double minimumValue,
73                                   final double maximumValue,
74                                   final ColorFactory colorFactory)
75      {
76          if (colors == null)
77          {
78              throw new IllegalArgumentException("colors must not be null");
79          }
80          if (colors.size() < 2)
81          {
82              throw new IllegalArgumentException("colors must contain at least two colors");
83          }
84          if (colorFactory == null)
85          {
86              throw new IllegalArgumentException("colorFactory must not be null");
87          }
88          this.name = name;
89          this.colors = new ArrayList<Color>(colors);
90          this.minimumValue = minimumValue;
91          this.maximumValue = maximumValue;
92          this.colorFactory = colorFactory;
93  
94          // continuous color scheme requires one anchor for each color
95          anchors = new ArrayList<Double>(colors.size());
96          double anchorLength = (Math.abs(maximumValue - minimumValue) / (colors.size() - 1));
97          for (int i = 0; i < colors.size(); i++)
98          {
99              anchors.add(Double.valueOf(i * anchorLength));
100         }
101     }
102 
103 
104     /**
105      * Return the name of this continuous color scheme.
106      *
107      * @return the name of this continuous color scheme
108      */
109     public String getName()
110     {
111         return name;
112     }
113 
114     @Override
115     public double getMinimumValue()
116     {
117         return minimumValue;
118     }
119 
120     @Override
121     public void setMinimumValue(final double minimumValue)
122     {
123         throw new UnsupportedOperationException("setMinimumValue operation not supported by this color scheme");
124     }
125 
126     @Override
127     public double getMaximumValue()
128     {
129         return maximumValue;
130     }
131 
132     @Override
133     public void setMaximumValue(final double maximumValue)
134     {
135         throw new UnsupportedOperationException("setMaximumValue operation not supported by this color scheme");
136     }
137 
138     @Override
139     public double getZeroValue()
140     {
141         return Double.NaN;
142     }
143 
144     @Override
145     public void setZeroValue(final double zeroValue)
146     {
147         throw new UnsupportedOperationException("setZeroValue operation not supported by this color scheme");
148     }
149 
150     @Override
151     public ColorFactory getColorFactory()
152     {
153         return colorFactory;
154     }
155 
156     @Override
157     public void setColorFactory(final ColorFactory colorFactory)
158     {
159         throw new UnsupportedOperationException("setMaximumValue operation not supported by this color scheme");
160     }
161 
162     /**
163      * Return the minimum color for this color scheme.
164      *
165      * @return the minimum color for this color scheme
166      */
167     private Color getMinimumColor()
168     {
169         return colors.get(0);
170     }
171 
172     /**
173      * Return the minimum anchor for this color scheme.
174      *
175      * @return the minimum anchor for this color scheme
176      */
177     private double getMinimumAnchor()
178     {
179         return anchors.get(0);
180     }
181 
182     /**
183      * Return the maximum color for this color scheme.
184      *
185      * @return the maximum color for this color scheme
186      */
187     private Color getMaximumColor()
188     {
189         return colors.get(colors.size() - 1);
190     }
191 
192     @Override
193     public Color getColor(final double value)
194     {
195         if (value < getMinimumAnchor())
196         {
197             return getMinimumColor();
198         }
199         Color lowerColor = getMinimumColor();
200         double lowerAnchor = getMinimumAnchor();
201         for (int i = 0; i < anchors.size(); i++)
202         {
203             double upperAnchor = anchors.get(i);
204             if (value == upperAnchor)
205             {
206                 return colors.get(i);
207             }
208             else if (value < upperAnchor)
209             {
210                 Color upperColor = colors.get(i);
211                 lowerAnchor = anchors.get(i - 1);
212                 lowerColor = colors.get(i - 1);
213                 int r = (int) interpolate(value, lowerAnchor, upperAnchor,
214                                          (double) lowerColor.getRed(), (double) upperColor.getRed());
215                 int g = (int) interpolate(value, lowerAnchor, upperAnchor,
216                                           (double) lowerColor.getGreen(), (double) upperColor.getGreen());
217                 int b = (int) interpolate(value, lowerAnchor, upperAnchor,
218                                           (double) lowerColor.getBlue(), (double) upperColor.getBlue());
219                 int a = (int) interpolate(value, lowerAnchor, upperAnchor,
220                                           (double) lowerColor.getAlpha(), (double) upperColor.getAlpha());
221                 return colorFactory.createColor(r, g, b, ((float) a) / 255.0f);
222             }
223         }
224         return getMaximumColor();
225     }
226 
227     /**
228      * Interpolate the specified value from the specified source range to the specified target range.
229      *
230      * @param value value to interpolate
231      * @param sourceMinimum source range minimum value
232      * @param sourceMaximum source range maximum value
233      * @param targetMinimum target range minimum value
234      * @param targetMaximum target range maximum value
235      * @return the specified value interpolated from the specified source range to the specified target range
236      */
237     private static double interpolate(final double value,
238                                       final double sourceMinimum,
239                                       final double sourceMaximum,
240                                       final double targetMinimum,
241                                       final double targetMaximum)
242     {
243         return targetMinimum + (targetMaximum - targetMinimum)
244             * ((value - sourceMinimum) / (sourceMaximum - sourceMinimum));
245     }
246 }