View Javadoc

1   /*
2    * ObjectLab, http://www.objectlab.co.uk/open is supporting JTreeMap.
3    * 
4    * Based in London, we are world leaders in the design and development 
5    * of bespoke applications for the securities financing markets.
6    * 
7    * <a href="http://www.objectlab.co.uk/open">Click here to learn more</a>
8    *           ___  _     _           _   _          _
9    *          / _ \| |__ (_) ___  ___| |_| |    __ _| |__
10   *         | | | | '_ \| |/ _ \/ __| __| |   / _` | '_ \
11   *         | |_| | |_) | |  __/ (__| |_| |__| (_| | |_) |
12   *          \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
13   *                   |__/
14   *
15   *                     www.ObjectLab.co.uk
16   *
17   * $Id: ColorProvider.java 69 2006-10-24 16:20:20Z benoitx $
18   * 
19   * Copyright 2006 the original author or authors.
20   *
21   * Licensed under the Apache License, Version 2.0 (the "License"); you may not
22   * use this file except in compliance with the License. You may obtain a copy of
23   * the License at
24   *
25   * http://www.apache.org/licenses/LICENSE-2.0
26   *
27   * Unless required by applicable law or agreed to in writing, software
28   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
29   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
30   * License for the specific language governing permissions and limitations under
31   * the License.
32   */
33  package net.sf.jtreemap.swing.provider;
34  
35  import java.awt.Color;
36  import java.awt.Graphics;
37  import java.util.Enumeration;
38  
39  import javax.swing.JPanel;
40  
41  import net.sf.jtreemap.swing.ColorProvider;
42  import net.sf.jtreemap.swing.DefaultValue;
43  import net.sf.jtreemap.swing.JTreeMap;
44  import net.sf.jtreemap.swing.TreeMapNode;
45  import net.sf.jtreemap.swing.Value;
46  
47  /**
48   * An HSB color space color provider for JTreeMap. Uses a specified function to
49   * map the values onto the HSB color space. The default is a linear function,
50   * but in my experience one of the logarithmic ones works best for this color
51   * space.
52   * 
53   * @author Andy Adamczak
54   */
55  public class HSBTreeMapColorProvider extends ColorProvider {
56      private static final int HSBVAL_SIZE = 3;
57  
58      /**
59       * 
60       */
61      private static final long serialVersionUID = 5009655580804320847L;
62  
63      /**
64       * @author Andy Adamczak
65       */
66      public enum ColorDistributionTypes {
67          Linear, Log, Exp, SquareRoot, CubicRoot
68      }
69  
70      /**
71       * @param treeMap
72       * @param color
73       */
74      public HSBTreeMapColorProvider(final JTreeMap treeMap, final Color color) {
75          this(treeMap, ColorDistributionTypes.Linear, color, color);
76      }
77  
78      /**
79       * @param treeMap
80       * @param colorDistribution
81       * @param color
82       */
83      public HSBTreeMapColorProvider(final JTreeMap treeMap, final ColorDistributionTypes colorDistribution, final Color color) {
84          this(treeMap, colorDistribution, color, color);
85      }
86  
87      /**
88       * @param treeMap
89       * @param positiveColor
90       * @param negativeColor
91       */
92      public HSBTreeMapColorProvider(final JTreeMap treeMap, final Color positiveColor, final Color negativeColor) {
93          this(treeMap, ColorDistributionTypes.Linear, positiveColor, negativeColor);
94      }
95  
96      /**
97       * @param treeMap
98       * @param colorDistribution
99       * @param positiveColor
100      * @param negativeColor
101      */
102     public HSBTreeMapColorProvider(final JTreeMap treeMap, final ColorDistributionTypes colorDistribution,
103             final Color positiveColor, final Color negativeColor) {
104         super();
105         jTreeMap = treeMap;
106         this.colorDistribution = colorDistribution;
107         adjustColor(positiveColor, negativeColor);
108     }
109 
110     /**
111      * @param treeMap
112      * @param hue
113      * @param saturation
114      */
115     public HSBTreeMapColorProvider(final JTreeMap treeMap, final float hue, final float saturation) {
116         this(treeMap, ColorDistributionTypes.Linear, hue, saturation, hue, saturation);
117     }
118 
119     /**
120      * @param treeMap
121      * @param colorDistribution
122      * @param hue
123      * @param saturation
124      */
125     public HSBTreeMapColorProvider(final JTreeMap treeMap, final ColorDistributionTypes colorDistribution, final float hue,
126             final float saturation) {
127         this(treeMap, colorDistribution, hue, saturation, hue, saturation);
128     }
129 
130     /**
131      * @param treeMap
132      * @param positiveHue
133      * @param positiveSaturation
134      * @param negativeHue
135      * @param negativeSaturation
136      */
137     public HSBTreeMapColorProvider(final JTreeMap treeMap, final float positiveHue, final float positiveSaturation,
138             final float negativeHue, final float negativeSaturation) {
139         this(treeMap, ColorDistributionTypes.Linear, positiveHue, positiveSaturation, negativeHue, negativeSaturation);
140     }
141 
142     /**
143      * @param treeMap
144      * @param colorDistribution
145      * @param positiveHue
146      * @param positiveSaturation
147      * @param negativeHue
148      * @param negativeSaturation
149      */
150     public HSBTreeMapColorProvider(final JTreeMap treeMap, final ColorDistributionTypes colorDistribution,
151             final float positiveHue, final float positiveSaturation, final float negativeHue, final float negativeSaturation) {
152         super();
153         jTreeMap = treeMap;
154         this.colorDistribution = colorDistribution;
155         adjustColor(positiveHue, positiveSaturation, negativeHue, negativeSaturation);
156     }
157 
158     /*
159      * (non-Javadoc)
160      * 
161      * @see net.sf.jtreemap.swing.ColorProvider#getLegendPanel()
162      */
163     @Override
164     public JPanel getLegendPanel() {
165         if (legend == null) {
166             legend = new Legend();
167         }
168 
169         return legend;
170     }
171 
172     /**
173      * @param color
174      */
175     public void adjustColor(final Color color) {
176         adjustColor(color, color);
177     }
178 
179     /**
180      * @param positiveColor
181      * @param negativeColor
182      */
183     public void adjustColor(final Color positiveColor, final Color negativeColor) {
184         // Figure out the hue of the passed in colors. Note, greys will map to
185         // reds in this color space, so use the
186         // hue/saturation
187         // constructions for grey scales.
188         float[] hsbvals = new float[HSBVAL_SIZE];
189 
190         hsbvals = Color.RGBtoHSB(positiveColor.getRed(), positiveColor.getGreen(), positiveColor.getBlue(), hsbvals);
191         positiveHue = hsbvals[0];
192         positiveSaturation = 1f;
193 
194         hsbvals = Color.RGBtoHSB(negativeColor.getRed(), negativeColor.getGreen(), negativeColor.getBlue(), hsbvals);
195         negativeHue = hsbvals[0];
196         negativeSaturation = 1f;
197     }
198 
199     /**
200      * @param hue
201      * @param saturation
202      */
203     public void adjustColor(final float hue, final float saturation) {
204         adjustColor(hue, saturation, hue, saturation);
205     }
206 
207     /**
208      * @param posHue
209      * @param posSaturation
210      * @param negHue
211      * @param negSaturation
212      */
213     public void adjustColor(final float posHue, final float posSaturation, final float negHue, final float negSaturation) {
214         this.positiveHue = posHue;
215         this.positiveSaturation = posSaturation;
216         this.negativeHue = negHue;
217         this.negativeSaturation = negSaturation;
218     }
219 
220     /*
221      * (non-Javadoc)
222      * 
223      * @see net.sf.jtreemap.swing.ColorProvider#getColor(net.sf.jtreemap.swing.Value)
224      */
225     @Override
226     public Color getColor(final Value value) {
227         // Figure out the current range of colors, map that range into a scale
228         // from 0 to 1,
229         // using the specified distribution type
230         if (maxValue == null || minValue == null) {
231             setValues(jTreeMap.getRoot());
232         }
233         final double max = this.maxValue.getValue();
234         final double min = this.minValue.getValue();
235         double val = (value != null ? value.getValue() : 0.00);
236 
237         if (val >= 0) {
238             // Value is greater than 0, use the positive colors
239             double range = max - Math.max(0, min);
240             val -= Math.max(0, min);
241             range = adjustValue(range);
242             return Color.getHSBColor(positiveHue, positiveSaturation, (float) (adjustValue(val) / range));
243         }
244 
245         // Value is less than 0, use the negative colors
246         double range = Math.abs(min - Math.min(0, max));
247         val += Math.min(0, max);
248         val = Math.abs(val);
249         // Value and range are not positive values, we need them to be for the
250         // math functions
251         range = adjustValue(range);
252         return Color.getHSBColor(negativeHue, negativeSaturation, (float) (adjustValue(val) / range));
253     }
254 
255     /**
256      * Given a value, maps that value to a new value using the specified math
257      * function
258      * 
259      * @param value
260      *            the value to convert
261      * @return the converted value
262      */
263     private double adjustValue(final double value) {
264         double ret = value;
265         switch (colorDistribution) {
266         case Log:
267             ret = Math.log1p(value);
268             break;
269         case Exp:
270             ret = Math.exp(value);
271             break;
272         case SquareRoot:
273             ret = Math.sqrt(value);
274             break;
275         case CubicRoot:
276             ret = Math.cbrt(value);
277             break;
278         default:
279             // Linear
280             ret = value;
281             break;
282         }
283         return ret;
284     }
285 
286     /**
287      * Set the max and the min values in the tree map
288      * 
289      * @param root
290      *            root of the JTreeMap
291      */
292     private void setValues(final TreeMapNode root) {
293         if (root.isLeaf()) {
294             final Value value = root.getValue();
295 
296             if (value == null) {
297                 return;
298             }
299 
300             if (maxValue == null || value.getValue() >= maxValue.getValue()) {
301                 try {
302                     final Class c = value.getClass();
303                     if (maxValue == null) {
304                         maxValue = (Value) (c.newInstance());
305                     }
306                     maxValue.setValue(value.getValue());
307                 } catch (final IllegalAccessException iae) {
308                     // ignore
309                 } catch (final InstantiationException ie) {
310                     // Ignore
311                     ie.printStackTrace();
312                 }
313             }
314 
315             if (minValue == null || value.getValue() <= minValue.getValue()) {
316                 try {
317                     final Class c = value.getClass();
318                     if (minValue == null) {
319                         minValue = (Value) (c.newInstance());
320                     }
321                     minValue.setValue(value.getValue());
322                 } catch (final IllegalAccessException iae) {
323                     // ignore
324                 } catch (final InstantiationException ie) {
325                     // Ignore
326                     ie.printStackTrace();
327                 }
328             }
329         } else {
330             for (final Enumeration e = root.children(); e.hasMoreElements();) {
331                 final TreeMapNode node = (TreeMapNode) e.nextElement();
332                 setValues(node);
333             }
334         }
335     }
336 
337     private JTreeMap jTreeMap;
338 
339     private JPanel legend;
340 
341     private Value maxValue;
342 
343     private Value minValue;
344 
345     private float positiveHue;
346 
347     private float negativeHue;
348 
349     private float positiveSaturation = 1f;
350 
351     private float negativeSaturation = 1f;
352 
353     private ColorDistributionTypes colorDistribution = ColorDistributionTypes.Linear;
354 
355     /**
356      * Panel with the legend
357      * 
358      * @author Laurent Dutheil
359      */
360     private class Legend extends JPanel {
361         private static final int Y_INSET = 7;
362 
363         private static final int X_INSET = 15;
364 
365         private static final long serialVersionUID = 6371342387871103592L;
366 
367         private static final int HEIGHT = 20;
368 
369         private static final int WIDTH = 120;
370 
371         private static final int X = 20;
372 
373         private static final int Y = 25;
374 
375         /**
376          * Constructor of Legend
377          */
378         public Legend() {
379             this.setSize(new java.awt.Dimension(2 * Legend.X + Legend.WIDTH, 2 * Legend.Y + Legend.HEIGHT));
380             this.setPreferredSize(new java.awt.Dimension(2 * Legend.X + Legend.WIDTH, 2 * Legend.Y + Legend.HEIGHT));
381         }
382 
383         @Override
384         public void paintComponent(final Graphics g) {
385             super.paintComponent(g);
386             if (HSBTreeMapColorProvider.this.minValue == null || HSBTreeMapColorProvider.this.maxValue == null) {
387                 setValues(HSBTreeMapColorProvider.this.jTreeMap.getRoot());
388             }
389             final Value min = HSBTreeMapColorProvider.this.minValue;
390             final Value max = HSBTreeMapColorProvider.this.maxValue;
391 
392             g.setColor(Color.black);
393             if (min != null && max != null) {
394                 g.drawString(min.getLabel(), Legend.X - X_INSET, Legend.Y - Y_INSET);
395                 g.drawString(max.getLabel(), Legend.X + Legend.WIDTH - X_INSET, Legend.Y - Y_INSET);
396 
397                 final double step = (max.getValue() - min.getValue()) / Legend.WIDTH;
398                 final Value value = new DefaultValue(min.getValue());
399                 for (int i = 0; i < Legend.WIDTH; i++) {
400                     g.setColor(HSBTreeMapColorProvider.this.getColor(value));
401                     g.fillRect(Legend.X + i, Legend.Y, 1, Legend.HEIGHT);
402                     value.setValue(value.getValue() + step);
403                 }
404             }
405         }
406     }
407 }
408 /*
409  *                 ObjectLab is supporing JTreeMap
410  * 
411  * Based in London, we are world leaders in the design and development 
412  * of bespoke applications for the securities financing markets.
413  * 
414  * <a href="http://www.objectlab.co.uk/open">Click here to learn more about us</a>
415  *           ___  _     _           _   _          _
416  *          / _ \| |__ (_) ___  ___| |_| |    __ _| |__
417  *         | | | | '_ \| |/ _ \/ __| __| |   / _` | '_ \
418  *         | |_| | |_) | |  __/ (__| |_| |__| (_| | |_) |
419  *          \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
420  *                   |__/
421  *
422  *                     www.ObjectLab.co.uk
423  */