    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2007, 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     * WaferMapRenderer.java
    029     * ---------------------
    030     * (C) Copyright 2003-2007, by Robert Redburn and Contributors.
    031     *
    032     * Original Author:  Robert Redburn;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *
    035     * $Id: WaferMapRenderer.java,v 1.6.2.4 2007/02/02 15:52:24 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 25-Nov-2003 : Version 1, contributed by Robert Redburn.  Changes have been
    040     *               made to fit the JFreeChart coding style (DG);
    041     * 20-Apr-2005 : Small update for changes to LegendItem class (DG);
    042     * ------------- JFREECHART 1.0.x ---------------------------------------------
    043     * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
    044     *
    045     */
    046
    047    package org.jfree.chart.renderer;
    048
    049    import java.awt.Color;
    050    import java.awt.Paint;
    051    import java.awt.Shape;
    052    import java.awt.Stroke;
    053    import java.awt.geom.Rectangle2D;
    054    import java.util.HashMap;
    055    import java.util.HashSet;
    056    import java.util.Iterator;
    057    import java.util.Map;
    058    import java.util.Set;
    059
    060    import org.jfree.chart.LegendItem;
    061    import org.jfree.chart.LegendItemCollection;
    062    import org.jfree.chart.plot.DrawingSupplier;
    063    import org.jfree.chart.plot.WaferMapPlot;
    064    import org.jfree.data.general.WaferMapDataset;
    065
    066    /**
    067     * A renderer for wafer map plots.  Provides color managment facilities.
    068     */
    069    public class WaferMapRenderer extends AbstractRenderer {
    070
    071        /** paint index */
    072        private Map paintIndex;
    073
    074        /** plot */
    075        private WaferMapPlot plot;
    076
    077        /** paint limit */
    078        private int paintLimit;
    079
    080        /** default paint limit */
    081        private static final int DEFAULT_PAINT_LIMIT = 35;
    082
    083        /** default multivalue paint calculation */
    084        public static final int POSITION_INDEX = 0;
    085
    086        /** The default value index. */
    087        public static final int VALUE_INDEX = 1;
    088
    089        /** paint index method */
    090        private int paintIndexMethod;
    091
    092        /**
    093         * Creates a new renderer.
    094         */
    095        public WaferMapRenderer() {
    096            this(null, null);
    097        }
    098
    099        /**
    100         * Creates a new renderer.
    101         *
    102         * @param paintLimit  the paint limit.
    103         * @param paintIndexMethod  the paint index method.
    104         */
    105        public WaferMapRenderer(int paintLimit, int paintIndexMethod) {
    106            this(new Integer(paintLimit), new Integer(paintIndexMethod));
    107        }
    108
    109        /**
    110         * Creates a new renderer.
    111         *
    112         * @param paintLimit  the paint limit.
    113         * @param paintIndexMethod  the paint index method.
    114         */
    115        public WaferMapRenderer(Integer paintLimit, Integer paintIndexMethod) {
    116
    117            super();
    118            this.paintIndex = new HashMap();
    119
    120            if (paintLimit == null) {
    121                this.paintLimit = DEFAULT_PAINT_LIMIT;
    122            }
    123            else {
    124                this.paintLimit = paintLimit.intValue();
    125            }
    126
    127            this.paintIndexMethod = VALUE_INDEX;
    128            if (paintIndexMethod != null) {
    129                if (isMethodValid(paintIndexMethod.intValue())) {
    130                    this.paintIndexMethod = paintIndexMethod.intValue();
    131                }
    132            }
    133        }
    134
    135        /**
    136         * Verifies that the passed paint index method is valid.
    137         *
    138         * @param method  the method.
    139         *
    140         * @return <code>true</code> or </code>false</code>.
    141         */
    142        private boolean isMethodValid(int method) {
    143            switch (method) {
    144                case POSITION_INDEX: return true;
    145                case VALUE_INDEX:    return true;
    146                default: return false;
    147            }
    148        }
    149
    150        /**
    151         * Returns the drawing supplier from the plot.
    152         *
    153         * @return The drawing supplier.
    154         */
    155        public DrawingSupplier getDrawingSupplier() {
    156            DrawingSupplier result = null;
    157            WaferMapPlot p = getPlot();
    158            if (p != null) {
    159                result = p.getDrawingSupplier();
    160            }
    161            return result;
    162        }
    163
    164        /**
    165         * Returns the plot.
    166         *
    167         * @return The plot.
    168         */
    169        public WaferMapPlot getPlot() {
    170            return this.plot;
    171        }
    172
    173        /**
    174         * Sets the plot and build the paint index.
    175         *
    176         * @param plot  the plot.
    177         */
    178        public void setPlot(WaferMapPlot plot) {
    179            this.plot = plot;
    180            makePaintIndex();
    181        }
    182
    183        /**
    184         * Returns the paint for a given chip value.
    185         *
    186         * @param value  the value.
    187         *
    188         * @return The paint.
    189         */
    190        public Paint getChipColor(Number value) {
    191            return getSeriesPaint(getPaintIndex(value));
    192        }
    193
    194        /**
    195         * Returns the paint index for a given chip value.
    196         *
    197         * @param value  the value.
    198         *
    199         * @return The paint index.
    200         */
    201        private int getPaintIndex(Number value) {
    202            return ((Integer) this.paintIndex.get(value)).intValue();
    203        }
    204
    205        /**
    206         * Builds a map of chip values to paint colors.
    207         * paintlimit is the maximum allowed number of colors.
    208         */
    209        private void makePaintIndex() {
    210            if (this.plot == null) {
    211                return;
    212            }
    213            WaferMapDataset data = this.plot.getDataset();
    214            Number dataMin = data.getMinValue();
    215            Number dataMax = data.getMaxValue();
    216            Set uniqueValues = data.getUniqueValues();
    217            if (uniqueValues.size() <= this.paintLimit) {
    218                int count = 0; // assign a color for each unique value
    219                for (Iterator i = uniqueValues.iterator(); i.hasNext();) {
    220                    this.paintIndex.put(i.next(), new Integer(count++));
    221                }
    222            }
    223            else {
    224                // more values than paints so map
    225                // multiple values to the same color
    226                switch (this.paintIndexMethod) {
    227                    case POSITION_INDEX:
    228                        makePositionIndex(uniqueValues);
    229                        break;
    230                    case VALUE_INDEX:
    231                        makeValueIndex(dataMax, dataMin, uniqueValues);
    232                        break;
    233                    default:
    234                        break;
    235                }
    236            }
    237        }
    238
    239        /**
    240         * Builds the paintindex by assigning colors based on the number
    241         * of unique values: totalvalues/totalcolors.
    242         *
    243         * @param uniqueValues  the set of unique values.
    244         */
    245        private void makePositionIndex(Set uniqueValues) {
    246            int valuesPerColor = (int) Math.ceil(
    247                (double) uniqueValues.size() / this.paintLimit
    248            );
    249            int count = 0; // assign a color for each unique value
    250            int paint = 0;
    251            for (Iterator i = uniqueValues.iterator(); i.hasNext();) {
    252                this.paintIndex.put(i.next(), new Integer(paint));
    253                if (++count % valuesPerColor == 0) {
    254                    paint++;
    255                }
    256                if (paint > this.paintLimit) {
    257                    paint = this.paintLimit;
    258                }
    259            }
    260        }
    261
    262        /**
    263         * Builds the paintindex by assigning colors evenly across the range
    264         * of values:  maxValue-minValue/totalcolors
    265         *
    266         * @param max  the maximum value.
    267         * @param min  the minumum value.
    268         * @param uniqueValues  the unique values.
    269         */
    270        private void makeValueIndex(Number max, Number min, Set uniqueValues) {
    271            double valueRange = max.doubleValue() - min.doubleValue();
    272            double valueStep = valueRange / this.paintLimit;
    273            int paint = 0;
    274            double cutPoint = min.doubleValue() + valueStep;
    275            for (Iterator i = uniqueValues.iterator(); i.hasNext();) {
    276                Number value = (Number) i.next();
    277                while (value.doubleValue() > cutPoint) {
    278                    cutPoint += valueStep;
    279                    paint++;
    280                    if (paint > this.paintLimit) {
    281                        paint = this.paintLimit;
    282                    }
    283                }
    284                this.paintIndex.put(value, new Integer(paint));
    285            }
    286        }
    287
    288        /**
    289         * Builds the list of legend entries.  called by getLegendItems in
    290         * WaferMapPlot to populate the plot legend.
    291         *
    292         * @return The legend items.
    293         */
    294        public LegendItemCollection getLegendCollection() {
    295            LegendItemCollection result = new LegendItemCollection();
    296            if (this.paintIndex != null && this.paintIndex.size() > 0) {
    297                if (this.paintIndex.size() <= this.paintLimit) {
    298                    for (Iterator i = this.paintIndex.entrySet().iterator();
    299                         i.hasNext();) {
    300                        // in this case, every color has a unique value
    301                        Map.Entry entry =  (Map.Entry) i.next();
    302                        String label = entry.getKey().toString();
    303                        String description = label;
    304                        Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d);
    305                        Paint paint = getSeriesPaint(
    306                            ((Integer) entry.getValue()).intValue()
    307                        );
    308                        Paint outlinePaint = Color.black;
    309                        Stroke outlineStroke = DEFAULT_STROKE;
    310
    311                        result.add(new LegendItem(label, description, null,
    312                                null, shape, paint, outlineStroke, outlinePaint));
    313
    314                    }
    315                }
    316                else {
    317                    // in this case, every color has a range of values
    318                    Set unique = new HashSet();
    319                    for (Iterator i = this.paintIndex.entrySet().iterator();
    320                         i.hasNext();) {
    321                        Map.Entry entry = (Map.Entry) i.next();
    322                        if (unique.add(entry.getValue())) {
    323                            String label = getMinPaintValue(
    324                                (Integer) entry.getValue()).toString()
    325                                + " - " + getMaxPaintValue(
    326                                    (Integer) entry.getValue()).toString();
    327                            String description = label;
    328                            Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d);
    329                            Paint paint = getSeriesPaint(
    330                                ((Integer) entry.getValue()).intValue()
    331                            );
    332                            Paint outlinePaint = Color.black;
    333                            Stroke outlineStroke = DEFAULT_STROKE;
    334
    335                            result.add(new LegendItem(label, description,
    336                                    null, null, shape, paint, outlineStroke,
    337                                    outlinePaint));
    338                        }
    339                    } // end foreach map entry
    340                } // end else
    341            }
    342            return result;
    343        }
    344
    345        /**
    346         * Returns the minimum chip value assigned to a color
    347         * in the paintIndex
    348         *
    349         * @param index  the index.
    350         *
    351         * @return The value.
    352         */
    353        private Number getMinPaintValue(Integer index) {
    354            double minValue = Double.POSITIVE_INFINITY;
    355            for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) {
    356                Map.Entry entry = (Map.Entry) i.next();
    357                if (((Integer) entry.getValue()).equals(index)) {
    358                    if (((Number) entry.getKey()).doubleValue() < minValue) {
    359                        minValue = ((Number) entry.getKey()).doubleValue();
    360                    }
    361                }
    362            }
    363            return new Double(minValue);
    364        }
    365
    366        /**
    367         * Returns the maximum chip value assigned to a color
    368         * in the paintIndex
    369         *
    370         * @param index  the index.
    371         *
    372         * @return The value
    373         */
    374        private Number getMaxPaintValue(Integer index) {
    375            double maxValue = Double.NEGATIVE_INFINITY;
    376            for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) {
    377                Map.Entry entry = (Map.Entry) i.next();
    378                if (((Integer) entry.getValue()).equals(index)) {
    379                    if (((Number) entry.getKey()).doubleValue() > maxValue) {
    380                        maxValue = ((Number) entry.getKey()).doubleValue();
    381                    }
    382                }
    383            }
    384            return new Double(maxValue);
    385        }
    386
    387
    388    } // end class wafermaprenderer