content: /* ===========================================================
   * JFreeChart : a free chart library for the Java(tm) platform
   * ===========================================================
   *
   * (C) Copyright 2000-2014, by Object Refinery Limited and Contributors.
   *
   * Project Info:  http://www.jfree.org/jfreechart/index.html
   *
   * This library is free software; you can redistribute it and/or modify it
   * under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation; either version 2.1 of the License, or
   * (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
   * License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
   * USA.
   *
   * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   * Other names may be trademarks of their respective owners.]
   *
   * ---------------------
   * WaferMapRenderer.java
   * ---------------------
   * (C) Copyright 2003-2014, by Robert Redburn and Contributors.
   *
   * Original Author:  Robert Redburn;
   * Contributor(s):   David Gilbert (for Object Refinery Limited);
   *
   * Changes
   * -------
   * 25-Nov-2003 : Version 1, contributed by Robert Redburn.  Changes have been
   *               made to fit the JFreeChart coding style (DG);
   * 20-Apr-2005 : Small update for changes to LegendItem class (DG);
   * ------------- JFREECHART 1.0.x ---------------------------------------------
   * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
   * 10-Mar-2014 : Removed LegendItemCollection (DG);
   * 
   */
  
  package org.jfree.chart.renderer;
  
  import java.awt.Color;
  import java.awt.Paint;
  import java.awt.Shape;
  import java.awt.Stroke;
  import java.awt.geom.Rectangle2D;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import org.jfree.chart.LegendItem;
  import org.jfree.chart.plot.DrawingSupplier;
  import org.jfree.chart.plot.WaferMapPlot;
  import org.jfree.data.general.WaferMapDataset;
  
  /**
   * A renderer for wafer map plots.  Provides color management facilities.
   */
  public class WaferMapRenderer extends AbstractRenderer {
  
      /** paint index */
      private Map<Number, Integer> paintIndex;
  
      /** plot */
      private WaferMapPlot plot;
  
      /** paint limit */
      private int paintLimit;
  
      /** default paint limit */
      private static final int DEFAULT_PAINT_LIMIT = 35;
  
      /** default multivalue paint calculation */
      public static final int POSITION_INDEX = 0;
  
      /** The default value index. */
      public static final int VALUE_INDEX = 1;
  
      /** paint index method */
      private int paintIndexMethod;
  
      /**
       * Creates a new renderer.
       */
      public WaferMapRenderer() {
          this(null, null);
      }
  
      /**
       * Creates a new renderer.
       *
       * @param paintLimit  the paint limit.
       * @param paintIndexMethod  the paint index method.
       */
      public WaferMapRenderer(Integer paintLimit, Integer paintIndexMethod) {
  
          super();
          this.paintIndex = new HashMap<Number, Integer>();
  
          if (paintLimit == null) {
              this.paintLimit = DEFAULT_PAINT_LIMIT;
          }
          else {
              this.paintLimit = paintLimit;
          }
  
          this.paintIndexMethod = VALUE_INDEX;
          if (paintIndexMethod != null) {
              if (isMethodValid(paintIndexMethod)) {
                  this.paintIndexMethod = paintIndexMethod;
              }
          }
      }
  
      /**
       * Verifies that the passed paint index method is valid.
       *
       * @param method  the method.
       *
       * @return <code>true</code> or </code>false</code>.
       */
      private boolean isMethodValid(int method) {
          switch (method) {
              case POSITION_INDEX: return true;
              case VALUE_INDEX:    return true;
              default: return false;
          }
      }
  
      /**
       * Returns the drawing supplier from the plot.
       *
       * @return The drawing supplier.
       */
      @Override
      public DrawingSupplier getDrawingSupplier() {
          DrawingSupplier result = null;
          WaferMapPlot p = getPlot();
          if (p != null) {
              result = p.getDrawingSupplier();
          }
          return result;
      }
  
      /**
       * Returns the plot.
       *
       * @return The plot.
       */
      public WaferMapPlot getPlot() {
          return this.plot;
      }
  
      /**
       * Sets the plot and build the paint index.
       *
       * @param plot  the plot.
       */
      public void setPlot(WaferMapPlot plot) {
          this.plot = plot;
          makePaintIndex();
      }
  
      /**
       * Returns the paint for a given chip value.
       *
       * @param value  the value.
       *
       * @return The paint.
       */
      public Paint getChipColor(Number value) {
          return getSeriesPaint(getPaintIndex(value));
      }
  
      /**
       * Returns the paint index for a given chip value.
       *
       * @param value  the value.
       *
       * @return The paint index.
       */
      private int getPaintIndex(Number value) {
          return this.paintIndex.get(value);
      }
  
      /**
       * Builds a map of chip values to paint colors.
       * paintlimit is the maximum allowed number of colors.
       */
      private void makePaintIndex() {
          if (this.plot == null) {
              return;
          }
          WaferMapDataset data = this.plot.getDataset();
          Number dataMin = data.getMinValue();
          Number dataMax = data.getMaxValue();
          Set<Number> uniqueValues = data.getUniqueValues();
          if (uniqueValues.size() <= this.paintLimit) {
              int count = 0; // assign a color for each unique value
              for (Number uniqueValue : uniqueValues) {
                  this.paintIndex.put(uniqueValue, count++);
              }
          }
          else {
              // more values than paints so map
              // multiple values to the same color
              switch (this.paintIndexMethod) {
                  case POSITION_INDEX:
                      makePositionIndex(uniqueValues);
                      break;
                  case VALUE_INDEX:
                      makeValueIndex(dataMax, dataMin, uniqueValues);
                      break;
                  default:
                      break;
              }
          }
      }
  
      /**
       * Builds the paintindex by assigning colors based on the number
       * of unique values: totalvalues/totalcolors.
       *
       * @param uniqueValues  the set of unique values.
       */
      private void makePositionIndex(Set<Number> uniqueValues) {
          int valuesPerColor = (int) Math.ceil(
              (double) uniqueValues.size() / this.paintLimit
          );
          int count = 0; // assign a color for each unique value
          int paint = 0;
          for (Number uniqueValue : uniqueValues) {
              this.paintIndex.put(uniqueValue, paint);
              if (++count % valuesPerColor == 0) {
                  paint++;
              }
              if (paint > this.paintLimit) {
                  paint = this.paintLimit;
              }
          }
      }
  
      /**
       * Builds the paintindex by assigning colors evenly across the range
       * of values:  maxValue-minValue/totalcolors
       *
       * @param max  the maximum value.
       * @param min  the minumum value.
       * @param uniqueValues  the unique values.
       */
      private void makeValueIndex(Number max, Number min, Set<Number> uniqueValues) {
          double valueRange = max.doubleValue() - min.doubleValue();
          double valueStep = valueRange / this.paintLimit;
          int paint = 0;
          double cutPoint = min.doubleValue() + valueStep;
          for (Number value : uniqueValues) {
              while (value.doubleValue() > cutPoint) {
                  cutPoint += valueStep;
                  paint++;
                  if (paint > this.paintLimit) {
                      paint = this.paintLimit;
                  }
              }
              this.paintIndex.put(value, paint);
          }
      }
  
      /**
       * Builds the list of legend entries.  called by getLegendItems in
       * WaferMapPlot to populate the plot legend.
       *
       * @return The legend items.
       */
      public List<LegendItem> getLegendCollection() {
          List<LegendItem> result = new ArrayList<LegendItem>();
          if (this.paintIndex != null && this.paintIndex.size() > 0) {
              if (this.paintIndex.size() <= this.paintLimit) {
                  for (Map.Entry<Number, Integer> entry : this.paintIndex.entrySet()) {
                      // in this case, every color has a unique value
                      String label = entry.getKey().toString();
                      String description = label;
                      Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d);
                      Paint paint = lookupSeriesPaint(
                              entry.getValue());
                      Paint outlinePaint = Color.BLACK;
                      Stroke outlineStroke = DEFAULT_STROKE;
  
                      result.add(new LegendItem(label, description, null,
                              null, shape, paint, outlineStroke, outlinePaint));
  
                  }
              }
              else {
                  // in this case, every color has a range of values
                  Set<Integer> unique = new HashSet<Integer>();
                  for (Map.Entry<Number, Integer> entry : this.paintIndex.entrySet()) {
                      if (unique.add(entry.getValue())) {
                          String label = getMinPaintValue(
                                  entry.getValue()).toString()
                                  + " - " + getMaxPaintValue(
                                  entry.getValue()).toString();
                          String description = label;
                          Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d);
                          Paint paint = getSeriesPaint(
                                  entry.getValue()
                          );
                          Paint outlinePaint = Color.BLACK;
                          Stroke outlineStroke = DEFAULT_STROKE;
  
                          result.add(new LegendItem(label, description,
                                  null, null, shape, paint, outlineStroke,
                                  outlinePaint));
                      }
                  } // end foreach map entry
              } // end else
          }
          return result;
      }
  
      /**
       * Returns the minimum chip value assigned to a color
       * in the paintIndex
       *
       * @param index  the index.
       *
       * @return The value.
       */
      private Number getMinPaintValue(Integer index) {
          double minValue = Double.POSITIVE_INFINITY;
          for (Map.Entry<Number, Integer> entry : this.paintIndex.entrySet()) {
              if (entry.getValue().equals(index)) {
                  if (entry.getKey().doubleValue() < minValue) {
                      minValue = entry.getKey().doubleValue();
                  }
              }
          }
          return minValue;
      }
  
      /**
       * Returns the maximum chip value assigned to a color
       * in the paintIndex
       *
       * @param index  the index.
       *
       * @return The value
       */
      private Number getMaxPaintValue(Integer index) {
          double maxValue = Double.NEGATIVE_INFINITY;
          for (Map.Entry<Number, Integer> entry : this.paintIndex.entrySet()) {
              if (entry.getValue().equals(index)) {
                  if (entry.getKey().doubleValue() > maxValue) {
                      maxValue = entry.getKey().doubleValue();
                  }
              }
          }
          return maxValue;
      }
  
  
  } // end class wafermaprenderer
  