    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    026 *
    027 * ---------------------
    028 * WaferMapRenderer.java
    029 * ---------------------
    030 * (C) Copyright 2003-2008, 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
    045package org.jfree.chart.renderer;
    046
    047import java.awt.Color;
    048import java.awt.Paint;
    049import java.awt.Shape;
    050import java.awt.Stroke;
    051import java.awt.geom.Rectangle2D;
    052import java.util.HashMap;
    053import java.util.HashSet;
    054import java.util.Iterator;
    055import java.util.Map;
    056import java.util.Set;
    057
    058import org.jfree.chart.LegendItem;
    059import org.jfree.chart.LegendItemCollection;
    060import org.jfree.chart.plot.DrawingSupplier;
    061import org.jfree.chart.plot.WaferMapPlot;
    062import org.jfree.data.general.WaferMapDataset;
    063
    064/**
    065 * A renderer for wafer map plots.  Provides color managment facilities.
    066 */
    067public 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 = lookupSeriesPaint(
    304                            ((Integer) entry.getValue()).intValue());
    305                    Paint outlinePaint = Color.black;
    306                    Stroke outlineStroke = DEFAULT_STROKE;
    307
    308                    result.add(new LegendItem(label, description, null,
    309                            null, shape, paint, outlineStroke, outlinePaint));
    310
    311                }
    312            }
    313            else {
    314                // in this case, every color has a range of values
    315                Set unique = new HashSet();
    316                for (Iterator i = this.paintIndex.entrySet().iterator();
    317                     i.hasNext();) {
    318                    Map.Entry entry = (Map.Entry) i.next();
    319                    if (unique.add(entry.getValue())) {
    320                        String label = getMinPaintValue(
    321                            (Integer) entry.getValue()).toString()
    322                            + " - " + getMaxPaintValue(
    323                                (Integer) entry.getValue()).toString();
    324                        String description = label;
    325                        Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d);
    326                        Paint paint = getSeriesPaint(
    327                            ((Integer) entry.getValue()).intValue()
    328                        );
    329                        Paint outlinePaint = Color.black;
    330                        Stroke outlineStroke = DEFAULT_STROKE;
    331
    332                        result.add(new LegendItem(label, description,
    333                                null, null, shape, paint, outlineStroke,
    334                                outlinePaint));
    335                    }
    336                } // end foreach map entry
    337            } // end else
    338        }
    339        return result;
    340    }
    341
    342    /**
    343     * Returns the minimum chip value assigned to a color
    344     * in the paintIndex
    345     *
    346     * @param index  the index.
    347     *
    348     * @return The value.
    349     */
    350    private Number getMinPaintValue(Integer index) {
    351        double minValue = Double.POSITIVE_INFINITY;
    352        for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) {
    353            Map.Entry entry = (Map.Entry) i.next();
    354            if (((Integer) entry.getValue()).equals(index)) {
    355                if (((Number) entry.getKey()).doubleValue() < minValue) {
    356                    minValue = ((Number) entry.getKey()).doubleValue();
    357                }
    358            }
    359        }
    360        return new Double(minValue);
    361    }
    362
    363    /**
    364     * Returns the maximum chip value assigned to a color
    365     * in the paintIndex
    366     *
    367     * @param index  the index.
    368     *
    369     * @return The value
    370     */
    371    private Number getMaxPaintValue(Integer index) {
    372        double maxValue = Double.NEGATIVE_INFINITY;
    373        for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) {
    374            Map.Entry entry = (Map.Entry) i.next();
    375            if (((Integer) entry.getValue()).equals(index)) {
    376                if (((Number) entry.getKey()).doubleValue() > maxValue) {
    377                    maxValue = ((Number) entry.getKey()).doubleValue();
    378                }
    379            }
    380        }
    381        return new Double(maxValue);
    382    }
    383
    384
    385} // end class wafermaprenderer