1 /*
2 * The baseCode project
3 *
4 * Copyright (c) 2006 University of British Columbia
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19 package ubic.basecode.graphics;
20
21 import java.awt.Color;
22
23 /**
24 * @author Will Braynen
25 *
26 */
27 public class ColorMap {
28 public static final Color DARK_RED = new Color( 128, 0, 0 );
29
30 public static final Color[] BLACKBODY_COLORMAP = { Color.black, DARK_RED, Color.orange, Color.yellow, Color.white };
31 public static final Color[] GREENRED_COLORMAP = { Color.green, Color.black, Color.red };
32
33 public static final int m_defaultSuggestedNumberOfColors = 32;
34 public static final Color[] REDGREEN_COLORMAP = { Color.red, Color.black, Color.green };
35 // color map
36 protected Color[] m_colorPalette;
37 protected Color[] m_currentColorMap = GREENRED_COLORMAP; // reference to a
38 protected Color[] m_customColorMap;
39
40 /** last color in the current color map */
41 protected Color m_maxColor;
42 /** first color in the current color map */
43 protected Color m_minColor;
44
45 public ColorMap() {
46
47 this( m_defaultSuggestedNumberOfColors );
48 }
49
50 public ColorMap( Color[] colorMap ) {
51
52 this( m_defaultSuggestedNumberOfColors, colorMap );
53 }
54
55 public ColorMap( int suggestedNumberOfColors ) {
56
57 this( suggestedNumberOfColors, GREENRED_COLORMAP );
58 }
59
60 /**
61 * Pre-condition: suggestedNumberOfColors > colorMap.length
62 *
63 * @param suggestedNumberOfColors int
64 * @param colorMap Color[]
65 */
66 public ColorMap( int suggestedNumberOfColors, Color[] colorMap ) {
67
68 m_currentColorMap = colorMap;
69 m_colorPalette = createColorPalette( suggestedNumberOfColors, colorMap );
70 }
71
72 public Color getColor( int i ) {
73
74 return m_colorPalette[i];
75 }
76
77 public Color[] getPalette() {
78
79 return m_colorPalette;
80 }
81
82 /**
83 * @return the number of colors in the palette
84 */
85 public int getPaletteSize() {
86
87 return m_colorPalette.length;
88 }
89
90 /**
91 * Allocates colors across a range.
92 *
93 * @param suggestedNumberOfColors palette resolution; if colorPalette.length does not evenly divide into this
94 * number, the actual number of colors in the palette will be rounded down.
95 * @param colorMap the simplest color map is { minColor, maxColor }; you might, however, want to go through
96 * intermediate colors instead of following a straight-line route through the color space.
97 * @return Color[] the color palette
98 */
99 protected Color[] createColorPalette( int suggestedNumberOfColors, Color[] colorMap ) {
100
101 Color[] colorPalette;
102 Color minColor;
103 Color maxColor;
104
105 // number of segments is one less than the number of points
106 // dividing the line into segments; the color map contains points,
107 // not segments, so for example, if the color map is trivially
108 // { minColor, maxColor }, then there is only one segment
109 int totalSegments = m_currentColorMap.length - 1;
110
111 // allocate colors across a range; distribute evenly
112 // between intermediate points, if there are any
113 int colorsPerSegment = suggestedNumberOfColors / totalSegments;
114
115 // make sure all segments are equal by rounding down
116 // the total number of colors if necessary
117 int totalColors = totalSegments * colorsPerSegment;
118
119 // create color map to return
120 colorPalette = new Color[totalColors];
121
122 for ( int segment = 0; segment < totalSegments; segment++ ) {
123 // the minimum color for each segment as defined by the current color
124 // map
125 minColor = colorMap[segment];
126 int r = minColor.getRed();
127 int g = minColor.getGreen();
128 int b = minColor.getBlue();
129
130 // the maximum color for each segment and the step sizes
131 maxColor = colorMap[segment + 1];
132 int redStepSize = getStepSize( r, maxColor.getRed(), colorsPerSegment );
133 int greenStepSize = getStepSize( g, maxColor.getGreen(), colorsPerSegment );
134 int blueStepSize = getStepSize( b, maxColor.getBlue(), colorsPerSegment );
135
136 for ( int k, i = 0; i < colorsPerSegment; i++ ) {
137 // clip
138 r = Math.min( r, 255 );
139 g = Math.min( g, 255 );
140 b = Math.min( b, 255 );
141
142 // but also make sure it's not less than zero
143 r = Math.max( r, 0 );
144 g = Math.max( g, 0 );
145 b = Math.max( b, 0 );
146
147 k = segment * colorsPerSegment + i;
148 colorPalette[k] = new Color( r, g, b );
149
150 r += redStepSize;
151 g += greenStepSize;
152 b += blueStepSize;
153 }
154 }
155
156 return colorPalette;
157
158 } // end createColorPalette
159
160 /**
161 * Calculate how fast we have to change color components. Assume min and max colors are different!
162 *
163 * @param minColor red, green, or blue component of the RGB color
164 * @param maxColor red, green, or blue component of the RGB color
165 * @param totalColors int
166 * @return positive or negative step size
167 */
168 protected int getStepSize( int minColor, int maxColor, int totalColors ) {
169
170 int colorRange = maxColor - minColor;
171 double stepSize = colorRange / ( double ) ( 1 == totalColors ? 1 : totalColors - 1 );
172 return ( int ) Math.round( stepSize );
173 }
174 }