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