/**
* Copyright (C) 2009, 2010 SC 4ViewSoft SRL
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.androsz.achartengine.chart;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import com.androsz.achartengine.model.MultipleCategorySeries;
import com.androsz.achartengine.renderer.DefaultRenderer;
import com.androsz.achartengine.renderer.SimpleSeriesRenderer;
/**
* The doughnut chart rendering class.
*/
public class DoughnutChart extends AbstractChart {
/**
*
*/
private static final long serialVersionUID = -1137709285814512986L;
/** The legend shape width. */
private static final int SHAPE_WIDTH = 10;
/** The series dataset. */
private final MultipleCategorySeries mDataset;
/** The series renderer. */
private final DefaultRenderer mRenderer;
/** A step variable to control the size of the legend shape. */
private int mStep;
/**
* Builds a new pie chart instance.
*
* @param dataset
* the series dataset
* @param renderer
* the series renderer
*/
public DoughnutChart(MultipleCategorySeries dataset,
DefaultRenderer renderer) {
mDataset = dataset;
mRenderer = renderer;
}
/**
* The graphical representation of the pie chart.
*
* @param canvas
* the canvas to paint to
* @param x
* the top left x value of the view to draw to
* @param y
* the top left y value of the view to draw to
* @param width
* the width of the view to draw to
* @param height
* the height of the view to draw to
* @param paint
* the paint
*/
@Override
public void draw(Canvas canvas, int x, int y, int width, int height,
Paint paint) {
paint.setAntiAlias(mRenderer.isAntialiasing());
paint.setStyle(Style.FILL);
paint.setTextSize(mRenderer.getLabelsTextSize());
int legendSize = mRenderer.getLegendHeight();
if (mRenderer.isShowLegend() && legendSize == 0) {
legendSize = height / 5;
}
final int left = x + 15;
final int top = y + 5;
final int right = x + width - 5;
final int bottom = y + height - legendSize;
drawBackground(mRenderer, canvas, x, y, width, height, paint, false,
DefaultRenderer.NO_COLOR);
mStep = SHAPE_WIDTH * 3 / 4;
final int cLength = mDataset.getCategoriesCount();
final int mRadius = Math.min(Math.abs(right - left),
Math.abs(bottom - top));
final double rCoef = 0.35;
final double decCoef = 0.2 / cLength;
int radius = (int) (mRadius * rCoef);
final int centerX = (left + right) / 2;
final int centerY = (bottom + top) / 2;
float shortRadius = radius * 0.9f;
final float longRadius = radius * 1.1f;
final String[] categories = new String[cLength];
for (int category = 0; category < cLength; category++) {
final int sLength = mDataset.getItemCount(category);
double total = 0;
final String[] titles = new String[sLength];
for (int i = 0; i < sLength; i++) {
total += mDataset.getValues(category)[i];
titles[i] = mDataset.getTitles(category)[i];
}
float currentAngle = 0;
RectF oval = new RectF(centerX - radius, centerY - radius, centerX
+ radius, centerY + radius);
float prevX2 = 0;
float prevY2 = 0;
final float minDist = 20;
float coef = 1;
for (int i = 0; i < sLength; i++) {
paint.setColor(mRenderer.getSeriesRendererAt(i).getColor());
final float value = (float) mDataset.getValues(category)[i];
final float angle = (float) (value / total * 360);
canvas.drawArc(oval, currentAngle, angle, true, paint);
if (mRenderer.isShowLabels()) {
paint.setColor(mRenderer.getLabelsColor());
final double rAngle = Math
.toRadians(90 - (currentAngle + angle / 2));
final double sinValue = Math.sin(rAngle);
final double cosValue = Math.cos(rAngle);
final int x1 = Math.round(centerX
+ (float) (shortRadius * sinValue));
final int y1 = Math.round(centerY
+ (float) (shortRadius * cosValue));
int x2 = Math.round(centerX
+ (float) (longRadius * sinValue));
int y2 = Math.round(centerY
+ (float) (longRadius * cosValue));
if (Math.sqrt((x2 - prevX2) * (x2 - prevX2) + (y2 - prevY2)
* (y2 - prevY2)) <= minDist) {
coef *= 1.1;
x2 = Math.round(centerX
+ (float) (longRadius * coef * sinValue));
y2 = Math.round(centerY
+ (float) (longRadius * coef * cosValue));
} else {
coef = 1;
}
canvas.drawLine(x1, y1, x2, y2, paint);
int extra = 10;
paint.setTextAlign(Align.LEFT);
if (x1 > x2) {
extra = -extra;
paint.setTextAlign(Align.RIGHT);
}
canvas.drawLine(x2, y2, x2 + extra, y2, paint);
canvas.drawText(mDataset.getTitles(category)[i],
x2 + extra, y2 + 5, paint);
prevX2 = x2;
prevY2 = y2;
}
currentAngle += angle;
}
radius -= mRadius * decCoef;
shortRadius -= mRadius * decCoef - 2;
if (mRenderer.getBackgroundColor() != 0) {
paint.setColor(mRenderer.getBackgroundColor());
} else {
paint.setColor(Color.WHITE);
}
paint.setStyle(Style.FILL);
oval = new RectF(centerX - radius, centerY - radius, centerX
+ radius, centerY + radius);
canvas.drawArc(oval, 0, 360, true, paint);
radius -= 1;
categories[category] = mDataset.getCategory(category);
}
drawLegend(canvas, mRenderer, categories, left, right, y, width,
height, legendSize, paint);
}
/**
* The graphical representation of the legend shape.
*
* @param canvas
* the canvas to paint to
* @param renderer
* the series renderer
* @param x
* the x value of the point the shape should be drawn at
* @param y
* the y value of the point the shape should be drawn at
* @param paint
* the paint to be used for drawing
*/
@Override
public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer,
float x, float y, Paint paint) {
mStep--;
canvas.drawCircle(x + SHAPE_WIDTH - mStep, y, mStep, paint);
}
/**
* Returns the legend shape width.
*
* @return the legend shape width
*/
@Override
public int getLegendShapeWidth() {
return SHAPE_WIDTH;
}
}