/**************************************************************************************** * Copyright (c) 2014 Michael Goldbach <michael@wildplot.com> * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 3 of the License, or (at your option) any later * * version. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************************/ package com.wildplot.android.rendering; import com.wildplot.android.rendering.graphics.wrapper.ColorWrap; import com.wildplot.android.rendering.graphics.wrapper.FontMetricsWrap; import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap; import com.wildplot.android.rendering.graphics.wrapper.RectangleWrap; import com.wildplot.android.rendering.interfaces.Drawable; import com.wildplot.android.rendering.interfaces.Legendable; //TODO reimplement this whole mess! public class PieChart implements Drawable, Legendable { // First sector starts at 12 o'clock. static final float FIRST_SECTOR_OFFSET = -90; private final PlotSheet mPlotSheet; private final double[] mValues; private final ColorWrap[] mColors; private String mName = ""; private boolean mNameIsSet = false; private double[] mPercent; private double mSum; public PieChart(PlotSheet plotSheet, double[] values, ColorWrap[] colors) { checkArguments(plotSheet, values, colors); mPlotSheet = plotSheet; mValues = values; mColors = colors; mPercent = new double[mValues.length]; for(double v: mValues) mSum +=v; double denominator = (mSum == 0) ? 1 : mSum; mPercent[0]= mValues[0]/ denominator; for(int i=1; i< mValues.length; i++){ mPercent[i]= mPercent[i-1]+ mValues[i]/ denominator; } } private void checkArguments(PlotSheet plotSheet, double[] values, ColorWrap[] colors) { if (plotSheet == null) { throw new NullPointerException(); } if (values.length != colors.length) { throw new IllegalArgumentException( "The number of colors must match the number of values"); } } @Override public boolean isOnFrame() { return false; } @Override public void paint(GraphicsWrap g) { //Do not show chart if segments are all zero if(mSum == 0) return; float maxSideBorders = Math.max(mPlotSheet.getFrameThickness()[PlotSheet.LEFT_FRAME_THICKNESS_INDEX], mPlotSheet.getFrameThickness()[PlotSheet.RIGHT_FRAME_THICKNESS_INDEX]); float maxUpperBottomBorders = Math.max(mPlotSheet.getFrameThickness()[PlotSheet.UPPER_FRAME_THICKNESS_INDEX], mPlotSheet.getFrameThickness()[PlotSheet.BOTTOM_FRAME_THICKNESS_INDEX]); float realBorder= Math.max(maxSideBorders, maxUpperBottomBorders) + 3; RectangleWrap field = g.getClipBounds(); float diameter = Math.min(field.width, field.height) - 2 * realBorder; float xCenter = field.width / 2.0F; float yCenter = field.height / 2.0F; ColorWrap oldColor = g.getColor(); drawSectors(g, diameter, xCenter, yCenter); drawSectorLabels(g, diameter, xCenter, yCenter); g.setColor(oldColor); } private void drawSectors(GraphicsWrap g, float diameter, float xCenter, float yCenter) { float left = xCenter - diameter / 2F; float top = yCenter - diameter / 2F; float currentAngle = FIRST_SECTOR_OFFSET; for (int i = 0; i < mPercent.length - 1; i++) { g.setColor(mColors[i]); float arcLength = (float) ((360 * mValues[i]) / mSum); g.fillArc(left, top, diameter, diameter, currentAngle, arcLength); currentAngle += arcLength; } //last one does need some corrections to fill a full circle: g.setColor(getLastSectorColor()); g.fillArc(left, top, diameter, diameter, currentAngle, (360F + FIRST_SECTOR_OFFSET - currentAngle)); g.setColor(ColorWrap.black); g.drawArc(left, top, diameter, diameter, 0, 360); } private ColorWrap getLastSectorColor() { return mColors[mColors.length - 1]; } private void drawSectorLabels(GraphicsWrap g, float diameter, float xCenter, float yCenter) { ColorWrap labelBackground = new ColorWrap(0, 0, 0, 0.5f); for(int j=0;j< mPercent.length;j++) { if (mValues[j] == 0) continue; double oldPercent = 0; if(j != 0) oldPercent = mPercent[j-1]; String text = ""+Math.round((((mPercent[j]- oldPercent))*100)*100)/100.0+"%"; float x = (float)(xCenter+Math.cos(-1*((oldPercent+(mPercent[j]- oldPercent)*0.5)*360+ FIRST_SECTOR_OFFSET)*Math.PI/180.0)*0.375*diameter)-20; float y = (float)(yCenter-Math.sin(-1*((oldPercent+(mPercent[j]- oldPercent)*0.5)*360+ FIRST_SECTOR_OFFSET)*Math.PI/180.0)*0.375*diameter); FontMetricsWrap fm = g.getFontMetrics(); float width = fm.stringWidth(text); float height = fm.getHeight(); g.setColor(labelBackground); g.fillRect(x-1,y-height+3,width+2,height); g.setColor(ColorWrap.white); g.drawString(text, x, y); } } @Override public void abortAndReset() {} @Override public boolean isClusterable() { return true; } @Override public boolean isCritical() { return false; } @Override public ColorWrap getColor() { return mColors.length > 0 ? mColors[0] : ColorWrap.WHITE; } @Override public String getName() { return mName; } @Override public boolean nameIsSet() { return mNameIsSet; } public void setName(String name){ mName = name; mNameIsSet = true; } }