001package com.hfg.graphics;
002
003import com.hfg.html.attribute.HTMLColor;
004import com.hfg.util.CompareUtil;
005import com.hfg.util.collection.CollectionUtil;
006import com.hfg.xml.XMLName;
007import com.hfg.xml.XMLTag;
008
009import java.awt.Color;
010import java.util.ArrayList;
011import java.util.List;
012
013//------------------------------------------------------------------------------
014/**
015 <div>
016 Color scale created from two or more colors.
017 </div>
018 <div>
019   Ex:
020   <pre>
021      ColorScale colorScale = new ColorScale(new Color[] {Color.WHITE, Color.BLACK, Color.BLUE, Color.GREEN});
022
023      Center center = body.addCenter();
024      center.addSpan("Color Scale Created from White, Black, Blue, &amp; Green");
025      Table table = center.addTable();
026      Tr row = table.addRow();
027
028      for (int i = 0; i &lt;= 20; i++)
029      {
030         float value = i * 0.05f;
031         HTMLColor scaleColor = new HTMLColor(colorScale.getColor(value));
032         row.addCell(String.format("%.2f", value)).setStyle(CSS.color(scaleColor.getContrastingColor()) + CSS.bgColor(scaleColor));
033      }
034   </pre>
035 </div>
036 <div style='margin-top:20px'>
037   <span>Color Scale Created from White, Black, Blue, &amp; Green</span>
038   <table summary='Example Color Scale'>
039     <tr>
040       <td style='color:#000000;background-color:#ffffff;'>0.00</td>
041       <td style='color:#000000;background-color:#d9d9d9;'>0.05</td>
042       <td style='color:#000000;background-color:#b3b3b3;'>0.10</td>
043       <td style='color:#000000;background-color:#8c8c8c;'>0.15</td>
044       <td style='color:#ffffff;background-color:#666666;'>0.20</td>
045       <td style='color:#ffffff;background-color:#404040;'>0.25</td>
046       <td style='color:#ffffff;background-color:#191919;'>0.30</td>
047       <td style='color:#ffffff;background-color:#00000d;'>0.35</td>
048       <td style='color:#ffffff;background-color:#000033;'>0.40</td>
049       <td style='color:#ffffff;background-color:#000059;'>0.45</td>
050       <td style='color:#ffffff;background-color:#000080;'>0.50</td>
051       <td style='color:#ffffff;background-color:#0000a6;'>0.55</td>
052       <td style='color:#ffffff;background-color:#0000cc;'>0.60</td>
053       <td style='color:#ffffff;background-color:#0000f2;'>0.65</td>
054       <td style='color:#ffffff;background-color:#0019e6;'>0.70</td>
055       <td style='color:#ffffff;background-color:#0040bf;'>0.75</td>
056       <td style='color:#ffffff;background-color:#006699;'>0.80</td>
057       <td style='color:#ffffff;background-color:#008c73;'>0.85</td>
058       <td style='color:#ffffff;background-color:#00b34c;'>0.90</td>
059       <td style='color:#000000;background-color:#00d926;'>0.95</td>
060       <td style='color:#000000;background-color:#00ff00;'>1.00</td>
061     </tr>
062   </table>
063 </div>
064 <div style='margin-top:20px'>
065   <span>Color Scale Created from Red, Yellow, &amp; Green</span>
066   <table summary='Example Color Scale'>
067     <tr>
068       <td style='color:#ffffff;background-color:#ff0000;'>0.00</td>
069       <td style='color:#ffffff;background-color:#ff1a00;'>0.05</td>
070       <td style='color:#ffffff;background-color:#ff3300;'>0.10</td>
071       <td style='color:#ffffff;background-color:#ff4d00;'>0.15</td>
072       <td style='color:#000000;background-color:#ff6600;'>0.20</td>
073       <td style='color:#000000;background-color:#ff8000;'>0.25</td>
074       <td style='color:#000000;background-color:#ff9900;'>0.30</td>
075       <td style='color:#000000;background-color:#ffb300;'>0.35</td>
076       <td style='color:#000000;background-color:#ffcc00;'>0.40</td>
077       <td style='color:#000000;background-color:#ffe600;'>0.45</td>
078       <td style='color:#000000;background-color:#ffff00;'>0.50</td>
079       <td style='color:#000000;background-color:#e6ff00;'>0.55</td>
080       <td style='color:#000000;background-color:#ccff00;'>0.60</td>
081       <td style='color:#000000;background-color:#b2ff00;'>0.65</td>
082       <td style='color:#000000;background-color:#99ff00;'>0.70</td>
083       <td style='color:#000000;background-color:#80ff00;'>0.75</td>
084       <td style='color:#000000;background-color:#66ff00;'>0.80</td>
085       <td style='color:#000000;background-color:#4cff00;'>0.85</td>
086       <td style='color:#000000;background-color:#33ff00;'>0.90</td>
087       <td style='color:#000000;background-color:#1aff00;'>0.95</td>
088       <td style='color:#000000;background-color:#00ff00;'>1.00</td>
089     </tr>
090   </table>
091 </div>
092 <div style='margin-top:20px'>
093   <span>Color Scale Created from White, Black, Blue, Red, Yellow, &amp; Green</span>
094   <table summary='Example Color Scale'>
095     <tr>
096       <td style='color:#000000;background-color:#ffffff;'>0.00</td>
097       <td style='color:#000000;background-color:#bfbfbf;'>0.05</td>
098       <td style='color:#000000;background-color:#808080;'>0.10</td>
099       <td style='color:#ffffff;background-color:#404040;'>0.15</td>
100       <td style='color:#ffffff;background-color:#000000;'>0.20</td>
101       <td style='color:#ffffff;background-color:#000040;'>0.25</td>
102       <td style='color:#ffffff;background-color:#000080;'>0.30</td>
103       <td style='color:#ffffff;background-color:#0000bf;'>0.35</td>
104       <td style='color:#ffffff;background-color:#0000ff;'>0.40</td>
105       <td style='color:#ffffff;background-color:#4000bf;'>0.45</td>
106       <td style='color:#ffffff;background-color:#800080;'>0.50</td>
107       <td style='color:#ffffff;background-color:#bf0040;'>0.55</td>
108       <td style='color:#ffffff;background-color:#ff0000;'>0.60</td>
109       <td style='color:#ffffff;background-color:#ff4000;'>0.65</td>
110       <td style='color:#000000;background-color:#ff7f00;'>0.70</td>
111       <td style='color:#000000;background-color:#ffbf00;'>0.75</td>
112       <td style='color:#000000;background-color:#ffff00;'>0.80</td>
113       <td style='color:#000000;background-color:#bfff00;'>0.85</td>
114       <td style='color:#000000;background-color:#7fff00;'>0.90</td>
115       <td style='color:#000000;background-color:#40ff00;'>0.95</td>
116       <td style='color:#000000;background-color:#00ff00;'>1.00</td>
117     </tr>
118   </table>
119 </div>
120
121 * @author J. Alex Taylor, hairyfatguy.com
122 */
123//------------------------------------------------------------------------------
124// com.hfg Library
125//
126// This library is free software; you can redistribute it and/or
127// modify it under the terms of the GNU Lesser General Public
128// License as published by the Free Software Foundation; either
129// version 2.1 of the License, or (at your option) any later version.
130//
131// This library is distributed in the hope that it will be useful,
132// but WITHOUT ANY WARRANTY; without even the implied warranty of
133// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
134// Lesser General Public License for more details.
135//
136// You should have received a copy of the GNU Lesser General Public
137// License along with this library; if not, write to the Free Software
138// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
139//
140// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
141// jataylor@hairyfatguy.com
142//------------------------------------------------------------------------------
143
144public class ColorScale implements ColorRule, Comparable
145{
146   private List<HTMLColor> mColors;
147
148   public static final XMLName XML_COLOR_SCALE   = new XMLName("ColorScale");
149   private static final XMLName XML_COLOR        = new XMLName("Color");
150
151   //##########################################################################
152   // CONSTRUCTORS
153   //##########################################################################
154
155   //--------------------------------------------------------------------------
156   public ColorScale(List<Color> inColors)
157   {
158      if (null == inColors
159          || inColors.size() < 2)
160      {
161         throw new RuntimeException("At least 2 colors must be specified!");
162      }
163
164      mColors = new ArrayList<>(inColors.size());
165      for (Color color : inColors)
166      {
167         mColors.add(new HTMLColor(color));
168      }
169   }
170
171   //--------------------------------------------------------------------------
172   public ColorScale(Color... inColors)
173   {
174      if (null == inColors
175          || inColors.length < 2)
176      {
177         throw new RuntimeException("At least 2 colors must be specified!");
178      }
179
180      mColors = new ArrayList<>(inColors.length);
181      for (Color color : inColors)
182      {
183         mColors.add(new HTMLColor(color));
184      }
185   }
186
187   //---------------------------------------------------------------------------
188   public ColorScale(XMLTag inXMLTag)
189   {
190      inXMLTag.verifyTagName(XML_COLOR_SCALE);
191
192      List<XMLTag> subTags = inXMLTag.getSubtagsByName(XML_COLOR);
193      if (CollectionUtil.hasValues(subTags))
194      {
195         mColors = new ArrayList<>(subTags.size());
196
197         for (XMLTag subTag : subTags)
198         {
199            mColors.add(HTMLColor.valueOf(subTag.getUnescapedContent()));
200         }
201      }
202   }
203
204   //##########################################################################
205   // PUBLIC METHODS
206   //##########################################################################
207
208   //--------------------------------------------------------------------------
209   public List<HTMLColor> getColors()
210   {
211      return mColors;
212   }
213
214   //--------------------------------------------------------------------------
215   /**
216    * Determines the color that should be assigned to the specified value that
217    * has been normalized to the range of 0 to 1.
218    * @param inValue 0 &lt;= float value &gt;= 1.0
219    * @return the assigned color
220    */
221   public Color assignColorForValue(float inValue)
222   {
223      if (inValue < 0 || inValue > 1.0)
224      {
225         throw new RuntimeException("The value to map to the color scale must be between 0 and 1!");
226      }
227
228      // Determine which two colors the value falls between.
229
230      float regionSize = 1.0f / (mColors.size() - 1);
231
232      int lowIndex = (int) Math.floor(inValue / regionSize);
233      if (lowIndex == mColors.size() - 1) lowIndex--;
234
235      return ColorUtil.blend(mColors.get(lowIndex), mColors.get(lowIndex + 1), 1 - ((inValue - (lowIndex * regionSize)) / regionSize));
236   }
237
238
239   //---------------------------------------------------------------------------
240   public XMLTag toXMLTag()
241   {
242      XMLTag tag = new XMLTag(XML_COLOR_SCALE);
243
244      for(Color color : mColors)
245      {
246         tag.addSubtag(new XMLTag(XML_COLOR).setContent(new HTMLColor(color).toString()));
247      }
248
249      return tag;
250   }
251
252
253   //--------------------------------------------------------------------------
254   @Override
255   public int compareTo(Object inObj2)
256   {
257      int result = -1;
258
259      if (inObj2 != null
260            && inObj2 instanceof ColorScale)
261      {
262         ColorScale colorScale2 = (ColorScale) inObj2;
263
264         result = CompareUtil.compare(mColors, colorScale2.mColors);
265      }
266
267      return result;
268   }
269}