sascha@3162: package de.intevation.flys.jfree;
sascha@3162: 
sascha@3162: /**
sascha@3162:  * Copyright (c) 2006, 2012 by Intevation GmbH
sascha@3162:  *
sascha@3162:  * @author Sascha L. Teichmann (teichmann@intevation.de)
sascha@3162:  *
sascha@3162:  * This program is free software under the LGPL (>=v2.1)
sascha@3162:  * Read the file LGPL coming with FLYS for details.
sascha@3162:  */
sascha@3162: 
sascha@3162: import java.awt.Font;
sascha@3162: import java.awt.Graphics2D;
sascha@3162: import java.awt.Paint;
sascha@3162: import java.awt.Shape;
sascha@3162: 
sascha@3162: import java.awt.geom.Point2D;
sascha@3162: import java.awt.geom.Rectangle2D;
sascha@3162: 
sascha@3162: import java.util.ArrayList;
sascha@3162: import java.util.List;
sascha@3162: import java.util.Map;
sascha@3162: 
sascha@3162: import org.jfree.chart.axis.ValueAxis;
sascha@3162: 
sascha@3162: import org.jfree.chart.labels.ItemLabelPosition;
sascha@3162: import org.jfree.chart.labels.XYItemLabelGenerator;
sascha@3162: 
sascha@3162: import org.jfree.chart.plot.CrosshairState;
sascha@3162: import org.jfree.chart.plot.PlotOrientation;
sascha@3162: import org.jfree.chart.plot.PlotRenderingInfo;
sascha@3162: import org.jfree.chart.plot.XYPlot;
sascha@3162: 
sascha@3162: import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
sascha@3162: import org.jfree.chart.renderer.xy.XYItemRendererState;
sascha@3162: 
sascha@3162: import org.jfree.data.xy.XYDataset;
sascha@3162: 
sascha@3162: import org.jfree.text.TextUtilities;
sascha@3162: 
sascha@3162: import org.jfree.ui.RectangleEdge;
sascha@3162: 
sascha@3162: public class ShapeRenderer
sascha@3162: extends      StandardXYItemRenderer {
sascha@3162: 
sascha@3162:     public static class Entry {
sascha@3162:         protected Shape   shape;
sascha@3162:         protected Shape   frame;
sascha@3162:         protected Paint   paint;
sascha@3162:         protected boolean filled;
sascha@3162: 
sascha@3162:         public Entry(
sascha@3162:             Shape shape,
sascha@3162:             Paint paint,
sascha@3162:             boolean filled
sascha@3162:         ) {
sascha@3162:             this.shape = shape;
sascha@3162:             this.paint = paint;
sascha@3162:             this.filled = filled;
sascha@3162:         }
sascha@3162: 
sascha@3162:         public Entry(
sascha@3162:             Shape   shape,
sascha@3162:             Shape   frame,
sascha@3162:             Paint   paint,
sascha@3162:             boolean filled
sascha@3162:         ) {
sascha@3162:             this.shape  = shape;
sascha@3162:             this.frame  = frame;
sascha@3162:             this.paint  = paint;
sascha@3162:             this.filled = filled;
sascha@3162:         }
sascha@3162: 
sascha@3162:         public Shape getShape() {
sascha@3162:             return shape;
sascha@3162:         }
sascha@3162: 
sascha@3162:         public void setShape(Shape shape) {
sascha@3162:             this.shape = shape;
sascha@3162:         }
sascha@3162: 
sascha@3162: 
sascha@3162:         public Paint getPaint() {
sascha@3162:             return paint;
sascha@3162:         }
sascha@3162: 
sascha@3162:         public void setPaint(Paint paint) {
sascha@3162:             this.paint = paint;
sascha@3162:         }
sascha@3162: 
sascha@3162:         public boolean getFilled() {
sascha@3162:             return filled;
sascha@3162:         }
sascha@3162: 
sascha@3162:         public void setFilled(boolean filled) {
sascha@3162:             this.filled = filled;
sascha@3162:         }
sascha@3162: 
sascha@3162:         public boolean equals(Object other) {
sascha@3162:             Entry entry = (Entry)other;
sascha@3162:             return filled == entry.filled
sascha@3162:                    &&   paint.equals(entry.paint)
sascha@3162:                    &&   shape.equals(entry.shape);
sascha@3162:         }
sascha@3162: 
sascha@3162:         public int hashCode() {
sascha@3162:             return
sascha@3162:                 shape.hashCode() ^
sascha@3162:                 paint.hashCode() ^
sascha@3162:                 (filled ? 1231 : 1237);
sascha@3162:         }
sascha@3162:     } // class Entry
sascha@3162: 
sascha@3173:     public interface LabelGenerator {
sascha@3173:         String createLabel(Entry entry);
sascha@3173:     } // interface EntryLabelGenerator
sascha@3173: 
sascha@3162:     protected Entry []  entries;
sascha@3162: 
sascha@3162:     protected List<Rectangle2D> labelBoundingBoxes;
sascha@3162: 
sascha@3162:     protected Rectangle2D area;
sascha@3162: 
sascha@3162:     public ShapeRenderer() {
sascha@3162:         this(SHAPES);
sascha@3162:     }
sascha@3162: 
sascha@3162:     public ShapeRenderer(int type) {
sascha@3162:         super(type);
sascha@3162:     }
sascha@3162: 
sascha@3162:     public ShapeRenderer(Map<Entry, Integer> map) {
sascha@3162:         super(SHAPES);
sascha@3162:         setEntries(map);
sascha@3162:     }
sascha@3162: 
sascha@3162:     public void setEntries(Entry [] entries) {
sascha@3162:         this.entries = entries;
sascha@3162:     }
sascha@3162: 
sascha@3162:     public void setEntries(Map<Entry, Integer> map) {
sascha@3162:         Entry [] entries = new Entry[map.size()];
sascha@3162: 
sascha@3162:         for (Map.Entry<Entry, Integer> entry: map.entrySet()) {
sascha@3162:             entries[entry.getValue()] = entry.getKey();
sascha@3162:         }
sascha@3162: 
sascha@3162:         setEntries(entries);
sascha@3162:     }
sascha@3162: 
sascha@3162:     @Override
sascha@3162:     public Shape getSeriesShape(int series) {
sascha@3162:         return entries[series].shape;
sascha@3162:     }
sascha@3162: 
sascha@3162:     public Shape getSeriesFrame(int series) {
sascha@3162:         return entries[series].frame;
sascha@3162:     }
sascha@3162: 
sascha@3162:     @Override
sascha@3162:     public Paint getSeriesPaint(int series) {
sascha@3162:         return entries[series].paint;
sascha@3162:     }
sascha@3162: 
sascha@3162:     @Override
sascha@3162:     public boolean getItemShapeFilled(int series, int item) {
sascha@3162:         return entries[series].filled;
sascha@3162:     }
sascha@3162: 
sascha@3162:     @Override
sascha@3162:     public XYItemRendererState initialise(
sascha@3162:         Graphics2D        g2,
sascha@3162:         Rectangle2D       dataArea,
sascha@3162:         XYPlot            plot,
sascha@3162:         XYDataset         data,
sascha@3162:         PlotRenderingInfo info
sascha@3162:     ) {
sascha@3162:         if (labelBoundingBoxes == null) {
sascha@3162:             labelBoundingBoxes = new ArrayList<Rectangle2D>(32);
sascha@3162:         }
sascha@3162:         else {
sascha@3162:             labelBoundingBoxes.clear();
sascha@3162:         }
sascha@3162: 
sascha@3162:         area = dataArea;
sascha@3162: 
sascha@3162:         return super.initialise(g2, dataArea, plot, data, info);
sascha@3162:     }
sascha@3162: 
sascha@3162:     @Override
sascha@3162:     public void drawItem(
sascha@3162:         Graphics2D          g2,
sascha@3162:         XYItemRendererState state,
sascha@3162:         Rectangle2D         dataArea,
sascha@3162:         PlotRenderingInfo   info,
sascha@3162:         XYPlot              plot,
sascha@3162:         ValueAxis           domainAxis,
sascha@3162:         ValueAxis           rangeAxis,
sascha@3162:         XYDataset           dataset,
sascha@3162:         int                 series,
sascha@3162:         int                 item,
sascha@3162:         CrosshairState      crosshairState,
sascha@3162:         int                 pass
sascha@3162:     ) {
sascha@3162:         if (!getItemVisible(series, item)) {
sascha@3162:             return;
sascha@3162:         }
sascha@3162: 
sascha@3162:         // get the data point...
sascha@3162:         double x1 = dataset.getXValue(series, item);
sascha@3162:         double y1 = dataset.getYValue(series, item);
sascha@3162:         if (Double.isNaN(x1) || Double.isNaN(y1)) {
sascha@3162:             return;
sascha@3162:         }
sascha@3162: 
sascha@3162:         RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
sascha@3162:         RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
sascha@3162:         double x = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
sascha@3162:         double y = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
sascha@3162: 
sascha@3162:         if (dataArea.contains(x, y))
sascha@3162:             super.drawItem(
sascha@3162:                 g2,
sascha@3162:                 state,
sascha@3162:                 dataArea,
sascha@3162:                 info,
sascha@3162:                 plot,
sascha@3162:                 domainAxis,
sascha@3162:                 rangeAxis,
sascha@3162:                 dataset,
sascha@3162:                 series,
sascha@3162:                 item,
sascha@3162:                 crosshairState,
sascha@3162:                 pass);
sascha@3162:     }
sascha@3162: 
sascha@3162:     protected Point2D shiftBox(Rectangle2D box) {
sascha@3162: 
sascha@3162:         double cx1 = area.getX();
sascha@3162:         double cy1 = area.getY();
sascha@3162:         double cx2 = cx1 + area.getWidth();
sascha@3162:         double cy2 = cy1 + area.getHeight();
sascha@3162: 
sascha@3162:         double bx1 = box.getX();
sascha@3162:         double by1 = box.getY();
sascha@3162:         double bx2 = bx1 + box.getWidth();
sascha@3162:         double by2 = by1 + box.getHeight();
sascha@3162: 
sascha@3162:         double dx;
sascha@3162:         double dy;
sascha@3162: 
sascha@3162:         if (bx1 < cx1) {
sascha@3162:             dx = cx1 - bx1;
sascha@3162:         }
sascha@3162:         else if (bx2 > cx2) {
sascha@3162:             dx = cx2 - bx2;
sascha@3162:         }
sascha@3162:         else {
sascha@3162:             dx = 0d;
sascha@3162:         }
sascha@3162: 
sascha@3162:         if (by1 < cy1) {
sascha@3162:             dy = cy1 - by1;
sascha@3162:         }
sascha@3162:         else if (by2 > cy2) {
sascha@3162:             dy = cy2 - by2;
sascha@3162:         }
sascha@3162:         else {
sascha@3162:             dy = 0d;
sascha@3162:         }
sascha@3162: 
sascha@3162:         return new Point2D.Double(dx, dy);
sascha@3162:     }
sascha@3162: 
sascha@3162:     @Override
sascha@3162:     protected void drawItemLabel(
sascha@3162:         Graphics2D      g2,
sascha@3162:         PlotOrientation orientation,
sascha@3162:         XYDataset       dataset,
sascha@3162:         int             series,
sascha@3162:         int             item,
sascha@3162:         double          x,
sascha@3162:         double          y,
sascha@3162:         boolean         negative
sascha@3162:     ) {
sascha@3162:         XYItemLabelGenerator generator = getItemLabelGenerator(series, item);
sascha@3162:         if (generator == null) {
sascha@3162:             return;
sascha@3162:         }
sascha@3162: 
sascha@3162:         Font labelFont = getItemLabelFont(series, item);
sascha@3162: 
sascha@3162:         Paint paint = getItemLabelPaint(series, item);
sascha@3162: 
sascha@3162:         g2.setFont(labelFont);
sascha@3162:         g2.setPaint(paint);
sascha@3162: 
sascha@3162:         String label = generator.generateLabel(dataset, series, item);
sascha@3162: 
sascha@3164:         ATTEMPS: for (int attempt = 0; attempt < 2; ++attempt) {
sascha@3164:             // get the label position..
sascha@3164:             ItemLabelPosition position = null;
sascha@3162: 
sascha@3164:             boolean pos;
sascha@3164:             switch (attempt) {
sascha@3164:                 case 0: pos = negative; break;
sascha@3164:                 case 1: pos = !negative; break;
sascha@3164:                 default: break ATTEMPS;
sascha@3164:             }
sascha@3162: 
sascha@3164:             if (pos) {
sascha@3164:                 position = getNegativeItemLabelPosition(series, item);
sascha@3162:             }
sascha@3164:             else {
sascha@3164:                 position = getPositiveItemLabelPosition(series, item);
sascha@3164:             }
sascha@3162: 
sascha@3164:             // work out the label anchor point...
sascha@3164:             Point2D anchorPoint = calculateLabelAnchorPoint(
sascha@3164:                 position.getItemLabelAnchor(), x, y, orientation);
sascha@3164: 
sascha@3164:             Shape labelShape = TextUtilities.calculateRotatedStringBounds(
sascha@3164:                 label, g2,
sascha@3164:                 (float)anchorPoint.getX(), (float)anchorPoint.getY(),
sascha@3164:                 position.getTextAnchor(), position.getAngle(),
sascha@3164:                 position.getRotationAnchor());
sascha@3164: 
sascha@3164:             Rectangle2D bbox = labelShape.getBounds2D();
sascha@3164: 
sascha@3164:             Point2D shift = shiftBox(bbox);
sascha@3164: 
sascha@3164:             bbox = new Rectangle2D.Double(
sascha@3164:                 bbox.getX() + shift.getX(),
sascha@3164:                 bbox.getY() + shift.getY(),
sascha@3164:                 bbox.getWidth(),
sascha@3164:                 bbox.getHeight());
sascha@3164: 
sascha@3164:             if (labelBoundingBoxes != null) {
sascha@3164:                 for (Rectangle2D old: labelBoundingBoxes) {
sascha@3164:                     if (old.intersects(bbox)) {
sascha@3164:                         continue ATTEMPS;
sascha@3164:                     }
sascha@3164:                 }
sascha@3164:                 labelBoundingBoxes.add(bbox);
sascha@3164:             }
sascha@3164: 
sascha@3164:             TextUtilities.drawRotatedString(
sascha@3164:                 label, g2,
sascha@3164:                 (float)(anchorPoint.getX() + shift.getX()),
sascha@3164:                 (float)(anchorPoint.getY() + shift.getY()),
sascha@3164:                 position.getTextAnchor(), position.getAngle(),
sascha@3164:                 position.getRotationAnchor());
sascha@3164:             break;
sascha@3164:         }
sascha@3162:     }
sascha@3162: }
sascha@3162: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :