org.gumtree.vis.mask.ChartMaskingUtilities.java Source code

Java tutorial

Introduction

Here is the source code for org.gumtree.vis.mask.ChartMaskingUtilities.java

Source

/*******************************************************************************
 * Copyright (c) 2010 Australian Nuclear Science and Technology Organisation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors: 
 *    Norman Xiong (nxi@Bragg Institute) - initial API and implementation
 ******************************************************************************/
package org.gumtree.vis.mask;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RectangularShape;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.Map.Entry;

import org.gumtree.vis.hist2d.Hist2DPanel;
import org.gumtree.vis.plot1d.Plot1DPanel;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.encoders.EncoderUtil;
import org.jfree.chart.encoders.ImageFormat;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.Range;
import org.jfree.ui.RectangleEdge;
import org.jfree.util.ShapeUtilities;

/**
 * @author nxi
 *
 */
public class ChartMaskingUtilities {

    private final static Font maskNameFont = new Font("Serif", Font.ITALIC, 10);
    private final static int maskDragPointHalfWidth = 2;
    private final static int maskDragPointWidth = 4;

    public static Rectangle2D getMaskFramework(AbstractMask mask, Rectangle2D imageArea, JFreeChart chart) {
        if (mask instanceof RangeMask) {
            return getDomainMaskFrame((RangeMask) mask, imageArea, chart);
        } else if (mask instanceof Abstract2DMask) {
            return translateChartRectangle((Abstract2DMask) mask, imageArea, chart).getRectangleFrame();
        } else {
            throw new IllegalArgumentException("must be Range mask or 2D mask");
        }
    }

    public static Abstract2DMask translateChartRectangle(Abstract2DMask mask, Rectangle2D imageArea,
            JFreeChart chart) {
        Rectangle2D bound = mask.getRectangleFrame();
        Point2D start = new Point2D.Double(bound.getMinX(), bound.getMinY());
        Point2D end = new Point2D.Double(bound.getMaxX(), bound.getMaxY());
        Point2D screenStart = translateChartPoint(start, imageArea, chart);
        Point2D screenEnd = translateChartPoint(end, imageArea, chart);
        Abstract2DMask imageMask = mask.clone();
        imageMask.setRectangleFrame(new Rectangle2D.Double(Math.min(screenStart.getX(), screenEnd.getX()),
                Math.min(screenStart.getY(), screenEnd.getY()), Math.abs(screenStart.getX() - screenEnd.getX()),
                Math.abs(screenStart.getY() - screenEnd.getY())));
        return imageMask;
    }

    public static Shape translateChartShape(Shape shape, Rectangle2D imageArea, JFreeChart chart) {
        if (shape instanceof Line2D) {
            Line2D line = (Line2D) shape;
            double length = line.getP1().distance(line.getP2());
            if (length == 0) {
                Point2D point = line.getP1();
                Point2D newPoint = ChartMaskingUtilities.translateChartPoint(point, imageArea, chart);
                Shape oShape = ShapeUtilities.createDiagonalCross(5f, 0.2f);
                //             Shape oShape = ShapeUtilities.createRegularCross(3f, 0.5f);
                Shape newShape = ShapeUtilities.createTranslatedShape(oShape, newPoint.getX(), newPoint.getY());
                return newShape;
            } else if (length < 1e-6) {
                if (line.getP1().getX() == line.getP2().getX()) {
                    double newX = ChartMaskingUtilities.translateChartPoint(line.getP1(), imageArea, chart).getX();
                    Line2D newLine = new Line2D.Double(newX, imageArea.getMinY(), newX, imageArea.getMaxY());
                    return newLine;
                } else {
                    double newY = ChartMaskingUtilities.translateChartPoint(line.getP1(), imageArea, chart).getY();
                    Line2D newLine = new Line2D.Double(imageArea.getMinX(), newY, imageArea.getMaxX(), newY);
                    return newLine;
                }
            }
            Line2D newShape = (Line2D) line.clone();
            Point2D newP1 = translateChartPoint(line.getP1(), imageArea, chart);
            Point2D newP2 = translateChartPoint(line.getP2(), imageArea, chart);
            newShape.setLine(newP1, newP2);
            return newShape;
        } else if (shape instanceof RectangularShape) {
            RectangularShape rect = (RectangularShape) shape;
            RectangularShape newShape = (RectangularShape) rect.clone();
            Rectangle2D bound = rect.getBounds2D();
            Point2D start = new Point2D.Double(bound.getMinX(), bound.getMinY());
            Point2D end = new Point2D.Double(bound.getMaxX(), bound.getMaxY());
            Point2D screenStart = translateChartPoint(start, imageArea, chart);
            Point2D screenEnd = translateChartPoint(end, imageArea, chart);
            newShape.setFrame(new Rectangle2D.Double(Math.min(screenStart.getX(), screenEnd.getX()),
                    Math.min(screenStart.getY(), screenEnd.getY()), Math.abs(screenStart.getX() - screenEnd.getX()),
                    Math.abs(screenStart.getY() - screenEnd.getY())));
            return newShape;
        } else {
            return shape;
        }
    }

    public static Rectangle2D getDomainMaskFrame(RangeMask mask, Rectangle2D imageArea, JFreeChart chart) {
        XYPlot plot = chart.getXYPlot();
        //       boolean isDomainInverted = plot.getDomainAxis().isInverted();
        //       Range domainRange = plot.getDomainAxis().getRange();
        //       Range imageRange = new Range(imageArea.getMinX(), imageArea.getMaxX());
        //       Range dataRange = translateDomainRange(mask.getRange(), 
        //             imageRange, domainRange, isDomainInverted);
        double lowerData = plot.getDomainAxis().valueToJava2D(mask.getMin(), imageArea, RectangleEdge.BOTTOM);
        double upperData = plot.getDomainAxis().valueToJava2D(mask.getMax(), imageArea, RectangleEdge.BOTTOM);
        return new Rectangle2D.Double(Math.min(lowerData, upperData), imageArea.getMinY(),
                Math.abs(upperData - lowerData), imageArea.getHeight());
    }

    public static Point2D translateChartPoint(Point2D point, Rectangle2D imageArea, JFreeChart chart) {
        XYPlot plot = chart.getXYPlot();
        double x, y;

        ValueAxis domainAxis = plot.getDomainAxis();
        ValueAxis rangeAxis = plot.getRangeAxis();

        x = domainAxis.valueToJava2D(point.getX(), imageArea, RectangleEdge.BOTTOM);
        y = rangeAxis.valueToJava2D(point.getY(), imageArea, RectangleEdge.LEFT);

        return new Point2D.Double(x, y);
    }

    public static Point2D translateChartPoint(Point2D point, Rectangle2D imageArea, JFreeChart chart,
            int rangeAxisIndex) {
        XYPlot plot = chart.getXYPlot();
        double x, y;

        ValueAxis domainAxis = plot.getDomainAxis();
        ValueAxis rangeAxis;
        if (rangeAxisIndex < 0 || rangeAxisIndex >= plot.getRangeAxisCount()) {
            rangeAxis = plot.getRangeAxis();
        } else {
            rangeAxis = plot.getRangeAxis(rangeAxisIndex);
        }
        x = domainAxis.valueToJava2D(point.getX(), imageArea, RectangleEdge.BOTTOM);
        y = rangeAxis.valueToJava2D(point.getY(), imageArea, RectangleEdge.LEFT);

        return new Point2D.Double(x, y);
    }

    //    public static Range translateDomainRange(Range dataRange, Range imageRange, Range plotRange, 
    //          boolean isInverted) {
    //       double lower, upper;
    //       if (!isInverted) {
    //          lower = imageRange.getLowerBound() + (dataRange.getLowerBound() - plotRange.getLowerBound()) 
    //                / plotRange.getLength() * imageRange.getLength();
    //          upper = imageRange.getLowerBound() + (dataRange.getUpperBound() - plotRange.getLowerBound()) 
    //               / plotRange.getLength() * imageRange.getLength();
    //       } else {
    //          lower = imageRange.getLowerBound() + (plotRange.getUpperBound() - dataRange.getUpperBound()) 
    //               / plotRange.getLength() * imageRange.getLength();
    //          upper = imageRange.getLowerBound() + (plotRange.getUpperBound() - dataRange.getLowerBound()) 
    //                / plotRange.getLength() * imageRange.getLength();
    //       }
    ////       Insets insets = getInsets();
    ////       return new Point2D.Double((x - insets.left) / this.scaleX, (y - insets.top) / this.scaleY);
    //       return new Range(lower, upper);
    //    }

    public static double translateScreenX(double screenX, Rectangle2D imageArea, JFreeChart chart) {
        //       XYPlot plot = chart.getXYPlot();
        //       boolean isDomainInverted = plot.getDomainAxis().isInverted();
        //       Range domainSection = plot.getDomainAxis().getRange();
        ValueAxis axis = chart.getXYPlot().getDomainAxis();

        return axis.java2DToValue(screenX, imageArea, RectangleEdge.BOTTOM);
        //       if (isDomainInverted) {
        //          return domainSection.getUpperBound() - (screenX - imageArea.getMinX()) 
        //                / imageArea.getWidth() * domainSection.getLength();
        //       } else {
        //          return (screenX - imageArea.getMinX()) / imageArea.getWidth() 
        //                * domainSection.getLength() + domainSection.getLowerBound();
        //       }
    }

    public static double translateScreenY(double screenY, Rectangle2D imageArea, JFreeChart chart,
            int rangeAxisIndex) {
        ValueAxis rangeAxis;
        if (rangeAxisIndex < 0 || rangeAxisIndex >= chart.getXYPlot().getRangeAxisCount()) {
            rangeAxis = chart.getXYPlot().getRangeAxis();
        } else {
            rangeAxis = chart.getXYPlot().getRangeAxis(rangeAxisIndex);
        }
        return rangeAxis.java2DToValue(screenY, imageArea, RectangleEdge.LEFT);
    }

    public static double translateChartY(double chartY, Rectangle2D imageArea, JFreeChart chart) {
        XYPlot plot = chart.getXYPlot();
        boolean isRangeInverted = plot.getRangeAxis().isInverted();
        Range rangeSection = plot.getRangeAxis().getRange();
        if (!isRangeInverted) {
            return imageArea.getMinY()
                    + (rangeSection.getUpperBound() - chartY) / rangeSection.getLength() * imageArea.getHeight();
        } else {
            return imageArea.getMinY()
                    + (chartY - rangeSection.getLowerBound()) / rangeSection.getLength() * imageArea.getHeight();
        }
    }

    /**
     * Draws mask rectangle (if present).
     * The drawing is performed in XOR mode, therefore
     * when this method is called twice in a row,
     * the second call will completely restore the state
     * of the canvas.
     *
     * @param g2 the graphics device.
     * @param xor  use XOR for drawing?
     */
    private static void drawMaskRectangle(Graphics2D g2, Rectangle2D imageArea, Abstract2DMask mask,
            Abstract2DMask selectedMask, JFreeChart chart, double fontSizeRate, Color fillColor) {
        g2.clip(imageArea);
        g2.setPaint(fillColor);
        Abstract2DMask translatedMask = translateChartRectangle(mask, imageArea, chart);
        if (translatedMask == null || translatedMask.getRectangleFrame().isEmpty()) {
            return;
        }
        drawMask(g2, translatedMask.getShape());
        if (mask == selectedMask) {
            drawMaskBoarder(g2, translatedMask);
        }
        drawMaskName(g2, translatedMask, imageArea, fontSizeRate);
    }

    private static void drawMask(Graphics2D g2, Shape mask) {
        g2.fill(mask);
    }

    private static void drawMaskBoarder(Graphics2D g2, Abstract2DMask mask) {
        g2.setPaint(Color.orange);
        g2.setStroke(new BasicStroke(1));
        Rectangle2D frame = mask.getRectangleFrame();
        g2.draw(frame);
        Rectangle2D dragPoint = new Rectangle2D.Double(frame.getMinX() - maskDragPointHalfWidth,
                frame.getMinY() - maskDragPointHalfWidth, maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getCenterX() - maskDragPointHalfWidth, frame.getMinY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMaxX() - maskDragPointHalfWidth, frame.getMinY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMaxX() - maskDragPointHalfWidth, frame.getCenterY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMinX() - maskDragPointHalfWidth, frame.getCenterY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMinX() - maskDragPointHalfWidth, frame.getMaxY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getCenterX() - maskDragPointHalfWidth, frame.getMaxY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMaxX() - maskDragPointHalfWidth, frame.getMaxY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        Color fillColor = new Color(250, 250, 50, 10);
        g2.setPaint(fillColor);
        g2.fill(mask.getShape());
    }

    public static void drawMaskBoarder(Graphics2D g2, Rectangle2D frame) {
        g2.setPaint(Color.orange);
        g2.setStroke(new BasicStroke(1));
        g2.draw(frame);
        Rectangle2D dragPoint = new Rectangle2D.Double(frame.getMinX() - maskDragPointHalfWidth,
                frame.getMinY() - maskDragPointHalfWidth, maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getCenterX() - maskDragPointHalfWidth, frame.getMinY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMaxX() - maskDragPointHalfWidth, frame.getMinY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMaxX() - maskDragPointHalfWidth, frame.getCenterY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMinX() - maskDragPointHalfWidth, frame.getCenterY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMinX() - maskDragPointHalfWidth, frame.getMaxY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getCenterX() - maskDragPointHalfWidth, frame.getMaxY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        dragPoint.setRect(frame.getMaxX() - maskDragPointHalfWidth, frame.getMaxY() - maskDragPointHalfWidth,
                maskDragPointWidth, maskDragPointWidth);
        g2.fill(dragPoint);
        Color fillColor = new Color(250, 250, 50, 30);
        g2.setPaint(fillColor);
        g2.fill(frame);
    }

    private static void drawMaskName(Graphics2D g2, AbstractMask mask, Rectangle2D imageArea, double fontSizeRate) {
        if (mask.getName() == null) {
            return;
        }
        Point2D fontLocation = mask.getTitleLocation(imageArea);
        g2.setPaint(Color.black);
        Font currentFont = g2.getFont();
        g2.setFont(currentFont.deriveFont((float) (maskNameFont.getSize() * fontSizeRate)).deriveFont(Font.ITALIC));
        g2.drawString(mask.getName(), (int) fontLocation.getX(), (int) fontLocation.getY());
        g2.setFont(currentFont);
    }

    /**
     * Writes a chart to an output stream in JPEG format. This method allows
     * you to pass in a {@link ChartRenderingInfo} object, to collect
     * information about the chart dimensions/entities.  You will need this
     * info if you want to create an HTML image map.
     *
     * @param out  the output stream (<code>null</code> not permitted).
     * @param chart  the chart (<code>null</code> not permitted).
     * @param width  the image width.
     * @param height  the image height.
     * @param info  the chart rendering info (<code>null</code> permitted).
     * @param shapeMap 
     *
     * @throws IOException if there are any I/O errors.
     */
    public static void writeChartAsJPEG(File file, JFreeChart chart, int width, int height, ChartRenderingInfo info,
            Rectangle2D imageArea, LinkedHashMap<AbstractMask, Color> maskList,
            LinkedHashMap<Shape, Color> shapeMap, LinkedHashMap<Rectangle2D, String> textContentMap)
            throws IOException {

        if (file == null) {
            throw new IllegalArgumentException("Null 'file' argument.");
        }
        if (chart == null) {
            throw new IllegalArgumentException("Null 'chart' argument.");
        }
        OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
        BufferedImage image = chart.createBufferedImage(width, height, BufferedImage.TYPE_INT_RGB, info);
        Graphics2D g2 = image.createGraphics();
        drawMasks(g2, imageArea, maskList, null, chart);
        drawShapes(g2, imageArea, shapeMap, chart);
        drawText(g2, imageArea, textContentMap, chart);
        g2.dispose();
        try {
            EncoderUtil.writeBufferedImage(image, ImageFormat.JPEG, out);
        } finally {
            out.close();
        }
    }

    /**
     * Writes a chart to an output stream in PNG format.  This method allows
     * you to pass in a {@link ChartRenderingInfo} object, to collect
     * information about the chart dimensions/entities.  You will need this
     * info if you want to create an HTML image map.
     *
     * @param out  the output stream (<code>null</code> not permitted).
     * @param chart  the chart (<code>null</code> not permitted).
     * @param width  the image width.
     * @param height  the image height.
     * @param info  carries back chart rendering info (<code>null</code>
     *              permitted).
     * @param shapeMap 
     * @param encodeAlpha  encode alpha?
     * @param compression  the PNG compression level (0-9).
     *
     * @throws IOException if there are any I/O errors.
     */
    public static void writeChartAsPNG(File file, JFreeChart chart, int width, int height, ChartRenderingInfo info,
            Rectangle2D imageArea, LinkedHashMap<AbstractMask, Color> maskList,
            LinkedHashMap<Shape, Color> shapeMap, LinkedHashMap<Rectangle2D, String> textContentMap)
            throws IOException {

        if (file == null) {
            throw new IllegalArgumentException("Null 'file' argument.");
        }
        if (chart == null) {
            throw new IllegalArgumentException("Null 'chart' argument.");
        }
        OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
        BufferedImage chartImage = chart.createBufferedImage(width, height, BufferedImage.TYPE_INT_ARGB, info);
        Graphics2D g2 = chartImage.createGraphics();
        drawMasks(g2, imageArea, maskList, null, chart);
        drawShapes(g2, imageArea, shapeMap, chart);
        drawText(g2, imageArea, textContentMap, chart);
        g2.dispose();
        try {
            ChartUtilities.writeBufferedImageAsPNG(out, chartImage);
        } finally {
            out.close();
        }
    }

    public static void drawMasks(Graphics2D g2, Rectangle2D imageArea, LinkedHashMap<AbstractMask, Color> maskList,
            AbstractMask selectedMask, JFreeChart chart) {
        drawMasks(g2, imageArea, maskList, selectedMask, chart, 1);
    }

    public static void drawMasks(Graphics2D g2, Rectangle2D imageArea, LinkedHashMap<AbstractMask, Color> maskList,
            AbstractMask selectedMask, JFreeChart chart, double fontSizeRate) {
        if (maskList == null) {
            return;
        }
        for (Entry<AbstractMask, Color> maskEntry : maskList.entrySet()) {
            AbstractMask mask = maskEntry.getKey();
            if (mask instanceof Abstract2DMask) {
                Color fillColor = mask.isInclusive() ? Hist2DPanel.MASK_INCLUSIVE_COLOR
                        : Hist2DPanel.MASK_EXCLUSIVE_COLOR;
                drawMaskRectangle(g2, imageArea, (Abstract2DMask) mask, (Abstract2DMask) selectedMask, chart,
                        fontSizeRate, fillColor);
            } else if (mask instanceof RangeMask) {
                //            Color fillColor = maskEntry.getValue();
                Color fillColor = mask.isInclusive() ? Plot1DPanel.MASK_INCLUSIVE_COLOR
                        : Plot1DPanel.MASK_EXCLUSIVE_COLOR;
                drawDomainMask(g2, imageArea, (RangeMask) mask, (RangeMask) selectedMask, chart, fontSizeRate,
                        fillColor);
            }
        }
    }

    private static void drawDomainMask(Graphics2D g2, Rectangle2D imageArea, RangeMask mask, RangeMask selectedMask,
            JFreeChart chart, double fontSizeRate, Color fillColor) {
        g2.clip(imageArea);
        g2.setPaint(fillColor);
        Rectangle2D translatedRectangle = getDomainMaskFrame(mask, imageArea, chart);
        if (translatedRectangle == null || translatedRectangle.isEmpty()) {
            return;
        }
        drawMask(g2, translatedRectangle);
        if (mask == selectedMask) {
            drawMaskBoarder(g2, translatedRectangle);
        }
        drawMaskName(g2, mask, translatedRectangle, fontSizeRate);
    }

    public static void drawShapes(Graphics2D g2, Rectangle2D imageArea, LinkedHashMap<Shape, Color> shapeList,
            JFreeChart chart) {
        if (shapeList == null) {
            return;
        }
        for (Entry<Shape, Color> shapeEntry : shapeList.entrySet()) {
            Shape shape = shapeEntry.getKey();
            Color color = shapeEntry.getValue();
            drawShape(g2, imageArea, shape, color, chart);
        }
    }

    public static void drawText(Graphics2D g2, Rectangle2D imageArea,
            LinkedHashMap<Rectangle2D, String> textContentMap, JFreeChart chart) {
        if (textContentMap == null || textContentMap.size() == 0) {
            return;
        }
        //      for (Entry<Rectangle2D, String> textEntry : textMap.entrySet()) {
        //         Rectangle2D rect = textEntry.getKey();
        //         String text = textEntry.getValue();
        //         drawText(g2, imageArea, rect, text, chart);
        //      }
        Color oldColor = g2.getColor();
        g2.setColor(Color.BLACK);
        for (Entry<Rectangle2D, String> entry : textContentMap.entrySet()) {
            Rectangle2D rect = entry.getKey();
            Point2D screenPoint = ChartMaskingUtilities
                    .translateChartPoint(new Point2D.Double(rect.getX(), rect.getY()), imageArea, chart);
            String text = entry.getValue();
            if (text == null) {
                continue;
            }
            String[] lines = text.split("\n");
            g2.setColor(Color.BLACK);
            for (int i = 0; i < lines.length; i++) {
                g2.drawString(lines[i], (int) screenPoint.getX() + 3, (int) screenPoint.getY() - 3 + i * 15);
            }
            //         if (rect == selectedTextWrapper) {
            //            FontMetrics fm = g2.getFontMetrics();
            //            int maxWidth = 0;
            //            int maxHeight = 0;
            //            for (int i = 0; i < lines.length; i++) {
            //               int lineWidth = fm.stringWidth(lines[i]);
            //               if (lineWidth > maxWidth) {
            //                  maxWidth = lineWidth;
            //               }
            //            }
            //            maxHeight = 15 * lines.length;
            //            if (maxWidth < 100) {
            //               maxWidth = 100;
            //            }
            //            Rectangle2D inputBox = new Rectangle2D.Double(screenPoint.getX(), screenPoint.getY() - 15, maxWidth + 8, maxHeight);
            //              Color fillColor = new Color(250, 250, 50, 30);
            //              g2.setPaint(fillColor);
            //              g2.fill(inputBox);
            //            g2.setColor(Color.ORANGE);
            //            g2.drawRect((int) screenPoint.getX(), (int) screenPoint.getY() - 15, maxWidth + 8, maxHeight);
            //
            //         }
            //         g2.drawString(text == null ? "" : text, (int) screenPoint.getX() + 3, (int) screenPoint.getY() - 3);
        }
        g2.setColor(oldColor);
    }

    public static void drawShapes(Graphics2D g2, Rectangle2D imageArea, Shape shape, JFreeChart chart) {
        Color color = Color.CYAN;
        Stroke oldStroke = g2.getStroke();
        g2.setStroke(new BasicStroke(2f));
        drawShape(g2, imageArea, shape, color, chart);
        g2.setStroke(oldStroke);
    }

    public static void drawShape(Graphics2D g2, Rectangle2D imageArea, Shape shape, Color color, JFreeChart chart) {
        g2.clip(imageArea);
        g2.setPaint(color);
        Shape newShape = translateChartShape(shape, imageArea, chart);
        if (shape == null) {
            return;
        }
        g2.draw(newShape);
    }

}