001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     *
027     * -----------------
028     * ColorPalette.java
029     * -----------------
030     * (C) Copyright 2002-2005, by David M. O'Donnell and Contributors.
031     *
032     * Original Author:  David M. O'Donnell;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *
035     * $Id: ColorPalette.java,v 1.1.2.1 2005/11/24 16:11:49 mungady Exp $
036     *
037     * Changes
038     * -------
039     * 26-Nov-2002 : Version 1 contributed by David M. O'Donnell (DG);
040     * 26-Mar-2003 : Implemented Serializable (DG);
041     * 14-Aug-2003 : Implemented Cloneable (DG);
042     *
043     */
044    
045    package org.jfree.chart.plot;
046    
047    import java.awt.Color;
048    import java.awt.Paint;
049    import java.io.Serializable;
050    import java.util.Arrays;
051    
052    import org.jfree.chart.axis.ValueTick;
053    
054    /**
055     * Defines palette used in Contour Plots.
056     *
057     * @author David M. O'Donnell.
058     */
059    public abstract class ColorPalette implements Cloneable, Serializable {
060    
061        /** For serialization. */
062        private static final long serialVersionUID = -9029901853079622051L;
063        
064        /** The min z-axis value. */
065        protected double minZ = -1;
066    
067        /** The max z-axis value. */
068        protected double maxZ = -1;
069    
070        /** Red components. */
071        protected int[] r;
072    
073        /** Green components. */
074        protected int[] g;
075    
076        /** Blue components. */
077        protected int[] b;
078    
079        /** Tick values are stored for use with stepped palette. */
080        protected double[] tickValues = null;
081    
082        /** Logscale? */
083        protected boolean logscale = false;
084    
085        /** Inverse palette (ie, min and max colors are reversed). */
086        protected boolean inverse = false;
087    
088        /** The palette name. */
089        protected String paletteName = null;
090    
091        /** Controls whether palette colors are stepped (not continuous). */
092        protected boolean stepped = false;
093    
094        /** Constant for converting loge to log10. */
095        protected static final double log10 = Math.log(10);
096        
097        /**
098         * Default contructor.
099         */
100        public ColorPalette() {
101            super();
102        }
103    
104        /**
105         * Returns the color associated with a value.
106         *
107         * @param value  the value.
108         *
109         * @return The color.
110         */
111        public Paint getColor(double value) {
112            int izV = (int) (253 * (value - this.minZ) 
113                        / (this.maxZ - this.minZ)) + 2;
114            return new Color(this.r[izV], this.g[izV], this.b[izV]);
115        }
116    
117        /**
118         * Returns a color.
119         *
120         * @param izV  ??.
121         *
122         * @return The color.
123         */
124        public Color getColor(int izV) {
125            return new Color(this.r[izV], this.g[izV], this.b[izV]);
126        }
127    
128        /**
129         * Returns Color by mapping a given value to a linear palette.
130         *
131         * @param value  the value.
132         *
133         * @return The color.
134         */
135        public Color getColorLinear(double value) {
136            int izV = 0;
137            if (this.stepped) {
138                int index = Arrays.binarySearch(this.tickValues, value);
139                if (index < 0) {
140                    index = -1 * index - 2;
141                }
142    
143                if (index < 0) { // For the case were the first tick is greater 
144                                 // than minZ
145                    value = this.minZ;
146                }
147                else {
148                    value = this.tickValues[index];
149                }
150            }
151            izV = (int) (253 * (value - this.minZ) / (this.maxZ - this.minZ)) + 2;
152            izV = Math.min(izV, 255);
153            izV = Math.max(izV, 2);
154            return getColor(izV);
155        }
156    
157        /**
158         * Returns Color by mapping a given value to a common log palette.
159         *
160         * @param value  the value.
161         *
162         * @return The color.
163         */
164        public Color getColorLog(double value) {
165            int izV = 0;
166            double minZtmp = this.minZ;
167            double maxZtmp = this.maxZ;
168            if (this.minZ <= 0.0) {
169    //          negatives = true;
170                this.maxZ = maxZtmp - minZtmp + 1;
171                this.minZ = 1;
172                value = value - minZtmp + 1;
173            }
174            double minZlog = Math.log(this.minZ) / log10;
175            double maxZlog = Math.log(this.maxZ) / log10;
176            value = Math.log(value) / log10;
177            //  value = Math.pow(10,value);
178            if (this.stepped) {
179                int numSteps = this.tickValues.length;
180                int steps = 256 / (numSteps - 1);
181                izV = steps * (int) (numSteps * (value - minZlog) 
182                        / (maxZlog - minZlog)) + 2;
183                //  izV = steps*numSteps*(int)((value/minZ)/(maxZlog-minZlog)) + 2;
184            }
185            else {
186                izV = (int) (253 * (value - minZlog) / (maxZlog - minZlog)) + 2;
187            }
188            izV = Math.min(izV, 255);
189            izV = Math.max(izV, 2);
190    
191            this.minZ = minZtmp;
192            this.maxZ = maxZtmp;
193    
194            return getColor(izV);
195        }
196    
197        /**
198         * Returns the maximum Z value.
199         *
200         * @return The value.
201         */
202        public double getMaxZ() {
203            return this.maxZ;
204        }
205    
206        /**
207         * Returns the minimum Z value.
208         *
209         * @return The value.
210         */
211        public double getMinZ() {
212            return this.minZ;
213        }
214    
215        /**
216         * Returns Paint by mapping a given value to a either a linear or common 
217         * log palette as controlled by the value logscale.
218         *
219         * @param value  the value.
220         *
221         * @return The paint.
222         */
223        public Paint getPaint(double value) {
224            if (isLogscale()) {
225                return getColorLog(value);
226            }
227            else {
228                return getColorLinear(value);
229            }
230        }
231    
232        /**
233         * Returns the palette name.
234         *
235         * @return The palette name.
236         */
237        public String getPaletteName () {
238            return this.paletteName;
239        }
240    
241        /**
242         * Returns the tick values.
243         *
244         * @return The tick values.
245         */
246        public double[] getTickValues() {
247            return this.tickValues;
248        }
249    
250        /**
251         * Called to initialize the palette's color indexes
252         */
253        public abstract void initialize();
254    
255        /**
256         * Inverts Palette
257         */
258        public void invertPalette() {
259    
260            int[] red = new int[256];
261            int[] green = new int[256];
262            int[] blue = new int[256];
263            for (int i = 0; i < 256; i++) {
264                red[i] = this.r[i];
265                green[i] = this.g[i];
266                blue[i] = this.b[i];
267            }
268    
269            for (int i = 2; i < 256; i++) {
270                this.r[i] = red[257 - i];
271                this.g[i] = green[257 - i];
272                this.b[i] = blue[257 - i];
273            }
274        }
275    
276        /**
277         * Returns the inverse flag.
278         *
279         * @return The flag.
280         */
281        public boolean isInverse () {
282            return this.inverse;
283        }
284    
285        /**
286         * Returns the log-scale flag.
287         *
288         * @return The flag.
289         */
290        public boolean isLogscale() {
291            return this.logscale;
292        }
293    
294        /**
295         * Returns the 'is-stepped' flag.
296         *
297         * @return The flag.
298         */
299        public boolean isStepped () {
300            return this.stepped;
301        }
302    
303        /**
304         * Sets the inverse flag.
305         *
306         * @param inverse  the new value.
307         */
308        public void setInverse (boolean inverse) {
309            this.inverse = inverse;
310            initialize();
311            if (inverse) {
312                invertPalette();
313            }
314            return;
315        }
316    
317        /**
318         * Sets the 'log-scale' flag.
319         *
320         * @param logscale  the new value.
321         */
322        public void setLogscale(boolean logscale) {
323            this.logscale = logscale;
324        }
325    
326        /**
327         * Sets the maximum Z value.
328         *
329         * @param newMaxZ  the new value.
330         */
331        public void setMaxZ(double newMaxZ) {
332            this.maxZ = newMaxZ;
333        }
334    
335        /**
336         * Sets the minimum Z value.
337         *
338         * @param newMinZ  the new value.
339         */
340        public void setMinZ(double newMinZ) {
341            this.minZ = newMinZ;
342        }
343    
344        /**
345         * Sets the palette name.
346         *
347         * @param paletteName  the name.
348         */
349        public void setPaletteName (String paletteName) {
350            //String oldValue = this.paletteName;
351            this.paletteName = paletteName;
352            return;
353        }
354    
355        /**
356         * Sets the stepped flag.
357         *
358         * @param stepped  the flag.
359         */
360        public void setStepped (boolean stepped) {
361            this.stepped = stepped;
362            return;
363        }
364    
365        /**
366         * Sets the tick values.
367         *
368         * @param newTickValues  the tick values.
369         */
370        public void setTickValues(double[] newTickValues) {
371            this.tickValues = newTickValues;
372        }
373    
374        /**
375         * Store ticks. Required when doing stepped axis
376         *
377         * @param ticks  the ticks.
378         */
379        public void setTickValues(java.util.List ticks) {
380            this.tickValues = new double[ticks.size()];
381            for (int i = 0; i < this.tickValues.length; i++) {
382                this.tickValues[i] = ((ValueTick) ticks.get(i)).getValue();
383            }
384        }
385    
386        /**
387         * Tests an object for equality with this instance.
388         * 
389         * @param o  the object to test.
390         * 
391         * @return A boolean.
392         */    
393        public boolean equals(Object o) {
394            if (this == o) {
395                return true;
396            }
397            if (!(o instanceof ColorPalette)) {
398                return false;
399            }
400    
401            ColorPalette colorPalette = (ColorPalette) o;
402    
403            if (this.inverse != colorPalette.inverse) {
404                return false;
405            }
406            if (this.logscale != colorPalette.logscale) {
407                return false;
408            }
409            if (this.maxZ != colorPalette.maxZ) {
410                return false;
411            }
412            if (this.minZ != colorPalette.minZ) {
413                return false;
414            }
415            if (this.stepped != colorPalette.stepped) {
416                return false;
417            }
418            if (!Arrays.equals(this.b, colorPalette.b)) {
419                return false;
420            }
421            if (!Arrays.equals(this.g, colorPalette.g)) {
422                return false;
423            }
424            if (this.paletteName != null 
425                    ? !this.paletteName.equals(colorPalette.paletteName) 
426                    : colorPalette.paletteName != null) {
427                return false;
428            }
429            if (!Arrays.equals(this.r, colorPalette.r)) {
430                return false;
431            }
432            if (!Arrays.equals(this.tickValues, colorPalette.tickValues)) {
433                return false;
434            }
435    
436            return true;
437        }
438    
439        /**
440         * Returns a hash code.
441         * 
442         * @return A hash code.
443         */
444        public int hashCode() {
445            int result;
446            long temp;
447            temp = Double.doubleToLongBits(this.minZ);
448            result = (int) (temp ^ (temp >>> 32));
449            temp = Double.doubleToLongBits(this.maxZ);
450            result = 29 * result + (int) (temp ^ (temp >>> 32));
451            result = 29 * result + (this.logscale ? 1 : 0);
452            result = 29 * result + (this.inverse ? 1 : 0);
453            result = 29 * result 
454                     + (this.paletteName != null ? this.paletteName.hashCode() : 0);
455            result = 29 * result + (this.stepped ? 1 : 0);
456            return result;
457        }
458    
459        /**
460         * Returns a clone of the palette.
461         * 
462         * @return A clone.
463         * 
464         * @throws CloneNotSupportedException never.
465         */
466        public Object clone() throws CloneNotSupportedException {
467            
468            ColorPalette clone = (ColorPalette) super.clone();
469            return clone;
470            
471        }
472    
473    }