/* 
 * This code is part of Project Waterloo from King's College London
 * 
 *
 * Copyright King's College London 2012-
 * 
 * @author Malcolm Lidierth, King's College London  [Contact]
 * 
 * Project Waterloo 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 3 of the License, or
 * (at your option) any later version.
 *
 * Project Waterloo 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 program.  If not, see .
 *
 */
package kcl.waterloo.graphics.plots2D;

import java.awt.Color;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;

import kcl.waterloo.graphics.GJAbstractGraph;
import static kcl.waterloo.graphics.plots2D.PieExtra.MODE.DEGREES;
import static kcl.waterloo.graphics.plots2D.PieExtra.MODE.PERCENTAGES;
import static kcl.waterloo.graphics.plots2D.PieExtra.MODE.QUANTITIES;
import static kcl.waterloo.graphics.plots2D.PieExtra.MODE.RADIANS;
import kcl.waterloo.math.ArrayMath;
import kcl.waterloo.math.geom.Cartesian;
import kcl.waterloo.math.geom.Coordinates;

/**
 * GJPie chart.
 *
 * @author malcolm
 */
public class GJPie extends GJAbstractPlot {

    private boolean newFlag = true;

    public GJPie() {
        super();
    }

    public GJPie(GJAbstractPlot p) {
        super(p);
    }

    public static GJPlotInterface createInstance() {
        GJPlotInterface instance = GJAbstractPlot.createInstance(new GJPie());
        instance.getDataModel().setExtraObject(new PieExtra());
        instance.setXData(new double[]{0d});
        return instance;
    }

    @Override
    public final void paintPlot(Graphics2D g2) {
        if (getParentGraph() == null || getDataModel() == null) {
            return;
        }

        /**
         * Note these changes will take effect only when the plot is first
         * created. The settings can be edited subsequently
         */
        if (newFlag) {
            newFlag = false;
            getParentGraph().setLeftAxisPainted(false);
            getParentGraph().setRightAxisPainted(false);
            getParentGraph().setTopAxisPainted(false);
            getParentGraph().setBottomAxisPainted(false);
            getParentGraph().setMajorGridPainted(false);
            getParentGraph().setMinorGridPainted(false);
            if (getParentGraph().getGraphContainer() != null) {
                getParentGraph().getGraphContainer().setAspectRatio(1d);
            }
            ((GJAbstractGraph) getParentGraph()).setAxesBounds(-1, -1, 2, 2);
        }

        super.paintPlot(g2);
        getScreenDataArray().clear();

        PieExtra extra = (PieExtra) getDataModel().getExtraObject();
        final double minDim = Math.min(((Component) this.getParentGraph()).getWidth(),
                ((Component) this.getParentGraph()).getHeight());

        final double outerDiameter = extra.getOuterRadius();
        double innerDiameter = extra.getInnerRadius();

        final double[] y = getYData().getRawDataValues();
        final double[] y2 = getYData().getRawDataValues();
        switch (extra.getMode()) {
            case PERCENTAGES:
                ArrayMath.muli(y, 3.6d);
                break;
            case RADIANS:
                ArrayMath.muli(y, 180d / Math.PI);
                break;
            case DEGREES:
                break;
            case QUANTITIES:
            default:
                ArrayMath.divi(y, ArrayMath.sum(y));
                ArrayMath.muli(y, 360d);
        }

        /**
         * Pinning the pie chart to a point on the axis allows it to be moved
         * with the axes.
         */
        double X = this.getParentGraph().xPositionToPixel(-1);
        double Y = this.getParentGraph().yPositionToPixel(1);

        /**
         * Create the center circle if required.
         */
        double xyTranslate = (minDim * outerDiameter - minDim * innerDiameter) / 2d;
        Area center = new Area(new Arc2D.Double(X + xyTranslate, Y + xyTranslate,
                minDim * innerDiameter,
                minDim * innerDiameter,
                0d, 360d, Arc2D.OPEN));

        /**
         * Fills each segment.
         */
        double sum = 0;
        double explodeExtent = extra.getExplodeExtent();
        for (int k = 0; k < y.length; k++) {
            Arc2D arc;
            double X2, Y2;
            if (extra.getExplode() != null && extra.getExplode().length > k
                    && extra.getExplode()[k]) {
                /**
                 * Translation for exploded segments
                 */
                double centerTheta = -(sum + y[k] / 2d) * (Math.PI / 180d);
                Cartesian a = Coordinates.polarToCartesian(centerTheta, outerDiameter + explodeExtent);
                Cartesian b = Coordinates.polarToCartesian(centerTheta, outerDiameter);
                double xoffset = minDim * (a.getX()[0] - b.getX()[0]);
                double yoffset = minDim * (a.getY()[0] - b.getY()[0]);
                X2 = X + xoffset;
                Y2 = Y + yoffset;
            } else {
                X2 = X;
                Y2 = Y;
            }
            arc = new Arc2D.Double(X2, Y2,
                    minDim * outerDiameter,
                    minDim * outerDiameter,
                    sum, y[k], Arc2D.PIE);
            g2.setPaint(getFill().get(k));
            g2.fill(arc);
            getScreenDataArray().add(arc);
            sum += y[k];
        }

        /**
         * Draws lines for each segment together with fill if "wheel" style.
         */
        for (int k = 0; k < getScreenDataArray().size(); k++) {
            g2.setPaint(getEdgeColor().get(k));
            g2.setStroke(getEdgeStroke().get(k));
            Arc2D arc = (Arc2D) getScreenDataArray().get(k);
            g2.draw(arc);

            if (innerDiameter != 0d) {
                g2.setPaint(Color.WHITE);
                g2.fill(center);
                g2.setPaint(Color.BLACK);
                g2.draw(center);
            }
        }

        /**
         * Add labels.
         */
        if (extra.isLabeled()) {
            FontMetrics metrics = g2.getFontMetrics();
            xyTranslate = (minDim * outerDiameter - minDim * extra.getLabelRadius()) / 2d;
            Arc2D lbl = new Arc2D.Double(X + xyTranslate, Y + xyTranslate,
                    minDim * extra.getLabelRadius(),
                    minDim * extra.getLabelRadius(),
                    0d, 360d, Arc2D.OPEN);
            sum = 0d;
            for (int k = 0; k < getScreenDataArray().size(); k++) {
                Arc2D arc = (Arc2D) getScreenDataArray().get(k);
                double theta = -(sum + y[k] / 2d) * (Math.PI / 180d);
                double xy[] = getXY(arc, lbl, theta);
                String s;
                if (extra.getLabels().size() > 0) {
                    s = extra.getLabels().get(k);
                } else if (extra.getLabelFormat() != null) {
                    s = extra.getLabelFormat().format(y2[k]);
                } else {
                    switch (extra.getMode()) {
                        case PERCENTAGES:
                            s = String.format("%3.1f%%", y2[k]);
                            break;
                        case RADIANS:
                            s = String.format("%3.2f", y2[k]);
                            break;
                        case DEGREES:
                        case QUANTITIES:
                        default:
                            s = String.format("%4.1f", y2[k]);
                    }
                }

                g2.drawString(s, (float) (xy[0] - metrics.stringWidth(s) / 2f), (float) (xy[1] + metrics.getMaxAscent() / 2d));
                sum += y[k];

            }
        }
    }

    private double[] getXY(Arc2D arc0, Arc2D arc1, double theta) {
        double x1 = arc0.getCenterX();
        double y1 = arc0.getCenterY();
        double x2 = arc0.getCenterX() + (Math.cos(theta) * arc1.getHeight());
        double y2 = arc0.getCenterY() + (Math.sin(theta) * arc1.getWidth());
        double horzAngle = (Math.atan2(y2 - y1, x2 - x1) + Math.PI * 2) % (Math.PI * 2);

        double xc = arc1.getCenterX();
        double yc = arc1.getCenterY();
        double r1 = arc1.getWidth() / 2d - arc1.getWidth() / 30d;
        double r2 = arc1.getHeight() / 2d - arc1.getHeight() / 30d;
        double cot = Math.cos(0);
        double sit = Math.sin(0);

        double xl = xc + r1 * Math.cos(horzAngle) * cot - r2 * Math.sin(horzAngle) * sit;
        double yl = yc + r1 * Math.cos(horzAngle) * sit + r2 * Math.sin(horzAngle) * cot;

        return new double[]{xl, yl};
    }
}

 

Ads help maintain this website.