aboutsummaryrefslogtreecommitdiff
path: root/MPChartLib/src
diff options
context:
space:
mode:
authoralmic <mick.ashton@flare-esports.net>2018-11-12 10:58:17 -0700
committeralmic <mick.ashton@flare-esports.net>2018-11-12 10:59:48 -0700
commit42cdba535f5af8076f3a376afba543c7e41eb9d0 (patch)
treec10fdd05a179f8c4f90153ddd303aaeb4eccc08f /MPChartLib/src
parent29f4cc5c2c5c7e5468759bb5ad414e9855cd09c6 (diff)
downloadMPAndroidChart-42cdba535f5af8076f3a376afba543c7e41eb9d0.tar.gz
Add Curved Slices to Pie Chart
Finally added support for rounded slices, and somewhat improved the code for the PieChartRenderer class.
Diffstat (limited to 'MPChartLib/src')
-rw-r--r--MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java10
-rw-r--r--MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java273
2 files changed, 164 insertions, 119 deletions
diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java
index 660fd29b..0ed45027 100644
--- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java
+++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java
@@ -651,6 +651,16 @@ public class PieChart extends PieRadarChartBase<PieData> {
}
/**
+ * Sets whether to draw slices in a curved fashion, only works if drawing the hole is enabled
+ * and if the slices are not drawn under the hole.
+ *
+ * @param enabled draw curved ends of slices
+ */
+ public void setDrawRoundedSlices(boolean enabled) {
+ mDrawRoundedSlices = enabled;
+ }
+
+ /**
* Returns true if the chart is set to draw each end of a pie-slice
* "rounded".
*
diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java
index b14657ce..c9f653e8 100644
--- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java
+++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java
@@ -230,6 +230,9 @@ public class PieChartRenderer extends DataRenderer {
final float userInnerRadius = drawInnerArc
? radius * (mChart.getHoleRadius() / 100.f)
: 0.f;
+ final float roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f;
+ final RectF roundedCircleBox = new RectF();
+ final boolean drawRoundedSlices = drawInnerArc && mChart.isDrawRoundedSlicesEnabled();
int visibleAngleCount = 0;
for (int j = 0; j < entryCount; j++) {
@@ -249,133 +252,151 @@ public class PieChartRenderer extends DataRenderer {
Entry e = dataSet.getEntryForIndex(j);
// draw only if the value is greater than zero
- if ((Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) {
+ if (!(Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) {
+ angle += sliceAngle * phaseX;
+ continue;
+ }
- if (!mChart.needsHighlight(j)) {
+ // Don't draw if it's highlighted, unless the chart uses rounded slices
+ if (mChart.needsHighlight(j) && !drawRoundedSlices) {
+ angle += sliceAngle * phaseX;
+ continue;
+ }
- final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f;
+ final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f;
- mRenderPaint.setColor(dataSet.getColor(j));
+ mRenderPaint.setColor(dataSet.getColor(j));
- final float sliceSpaceAngleOuter = visibleAngleCount == 1 ?
- 0.f :
- sliceSpace / (Utils.FDEG2RAD * radius);
- final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY;
- float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY;
- if (sweepAngleOuter < 0.f) {
- sweepAngleOuter = 0.f;
- }
+ final float sliceSpaceAngleOuter = visibleAngleCount == 1 ?
+ 0.f :
+ sliceSpace / (Utils.FDEG2RAD * radius);
+ final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY;
+ float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY;
+ if (sweepAngleOuter < 0.f) {
+ sweepAngleOuter = 0.f;
+ }
- mPathBuffer.reset();
+ mPathBuffer.reset();
- float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD);
- float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD);
+ if (drawRoundedSlices) {
+ float x = center.x + (radius - roundedRadius) * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD);
+ float y = center.y + (radius - roundedRadius) * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD);
+ roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius);
+ }
- if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) {
- // Android is doing "mod 360"
- mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW);
- } else {
+ float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD);
+ float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD);
- mPathBuffer.moveTo(arcStartPointX, arcStartPointY);
+ if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) {
+ // Android is doing "mod 360"
+ mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW);
+ } else {
- mPathBuffer.arcTo(
- circleBox,
- startAngleOuter,
- sweepAngleOuter
- );
- }
+ if (drawRoundedSlices) {
+ mPathBuffer.arcTo(roundedCircleBox, startAngleOuter + 180, -180);
+ }
- // API < 21 does not receive floats in addArc, but a RectF
- mInnerRectBuffer.set(
- center.x - innerRadius,
- center.y - innerRadius,
- center.x + innerRadius,
- center.y + innerRadius);
-
- if (drawInnerArc &&
- (innerRadius > 0.f || accountForSliceSpacing)) {
-
- if (accountForSliceSpacing) {
- float minSpacedRadius =
- calculateMinimumRadiusForSpacedSlice(
- center, radius,
- sliceAngle * phaseY,
- arcStartPointX, arcStartPointY,
- startAngleOuter,
- sweepAngleOuter);
-
- if (minSpacedRadius < 0.f)
- minSpacedRadius = -minSpacedRadius;
-
- innerRadius = Math.max(innerRadius, minSpacedRadius);
- }
+ mPathBuffer.arcTo(
+ circleBox,
+ startAngleOuter,
+ sweepAngleOuter
+ );
+ }
- final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ?
- 0.f :
- sliceSpace / (Utils.FDEG2RAD * innerRadius);
- final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY;
- float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY;
- if (sweepAngleInner < 0.f) {
- sweepAngleInner = 0.f;
- }
- final float endAngleInner = startAngleInner + sweepAngleInner;
-
- if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) {
- // Android is doing "mod 360"
- mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW);
- } else {
-
- mPathBuffer.lineTo(
- center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD),
- center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD));
-
- mPathBuffer.arcTo(
- mInnerRectBuffer,
- endAngleInner,
- -sweepAngleInner
- );
- }
- } else {
+ // API < 21 does not receive floats in addArc, but a RectF
+ mInnerRectBuffer.set(
+ center.x - innerRadius,
+ center.y - innerRadius,
+ center.x + innerRadius,
+ center.y + innerRadius);
- if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) {
- if (accountForSliceSpacing) {
-
- float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f;
-
- float sliceSpaceOffset =
- calculateMinimumRadiusForSpacedSlice(
- center,
- radius,
- sliceAngle * phaseY,
- arcStartPointX,
- arcStartPointY,
- startAngleOuter,
- sweepAngleOuter);
-
- float arcEndPointX = center.x +
- sliceSpaceOffset * (float) Math.cos(angleMiddle * Utils.FDEG2RAD);
- float arcEndPointY = center.y +
- sliceSpaceOffset * (float) Math.sin(angleMiddle * Utils.FDEG2RAD);
-
- mPathBuffer.lineTo(
- arcEndPointX,
- arcEndPointY);
-
- } else {
- mPathBuffer.lineTo(
- center.x,
- center.y);
- }
- }
+ if (drawInnerArc && (innerRadius > 0.f || accountForSliceSpacing)) {
- }
+ if (accountForSliceSpacing) {
+ float minSpacedRadius =
+ calculateMinimumRadiusForSpacedSlice(
+ center, radius,
+ sliceAngle * phaseY,
+ arcStartPointX, arcStartPointY,
+ startAngleOuter,
+ sweepAngleOuter);
- mPathBuffer.close();
+ if (minSpacedRadius < 0.f)
+ minSpacedRadius = -minSpacedRadius;
- mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint);
+ innerRadius = Math.max(innerRadius, minSpacedRadius);
}
+
+ final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ?
+ 0.f :
+ sliceSpace / (Utils.FDEG2RAD * innerRadius);
+ final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY;
+ float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY;
+ if (sweepAngleInner < 0.f) {
+ sweepAngleInner = 0.f;
+ }
+ final float endAngleInner = startAngleInner + sweepAngleInner;
+
+ if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) {
+ // Android is doing "mod 360"
+ mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW);
+ } else {
+
+ if (drawRoundedSlices) {
+ float x = center.x + (radius - roundedRadius) * (float) Math.cos(endAngleInner * Utils.FDEG2RAD);
+ float y = center.y + (radius - roundedRadius) * (float) Math.sin(endAngleInner * Utils.FDEG2RAD);
+ roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius);
+ mPathBuffer.arcTo(roundedCircleBox, endAngleInner, 180);
+ } else
+ mPathBuffer.lineTo(
+ center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD),
+ center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD));
+
+ mPathBuffer.arcTo(
+ mInnerRectBuffer,
+ endAngleInner,
+ -sweepAngleInner
+ );
+ }
+ } else {
+
+ if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) {
+ if (accountForSliceSpacing) {
+
+ float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f;
+
+ float sliceSpaceOffset =
+ calculateMinimumRadiusForSpacedSlice(
+ center,
+ radius,
+ sliceAngle * phaseY,
+ arcStartPointX,
+ arcStartPointY,
+ startAngleOuter,
+ sweepAngleOuter);
+
+ float arcEndPointX = center.x +
+ sliceSpaceOffset * (float) Math.cos(angleMiddle * Utils.FDEG2RAD);
+ float arcEndPointY = center.y +
+ sliceSpaceOffset * (float) Math.sin(angleMiddle * Utils.FDEG2RAD);
+
+ mPathBuffer.lineTo(
+ arcEndPointX,
+ arcEndPointY);
+
+ } else {
+ mPathBuffer.lineTo(
+ center.x,
+ center.y);
+ }
+ }
+
}
+ mPathBuffer.close();
+
+ mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint);
+
angle += sliceAngle * phaseX;
}
@@ -396,11 +417,17 @@ public class PieChartRenderer extends DataRenderer {
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
+ final float roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f;
final float holeRadiusPercent = mChart.getHoleRadius() / 100.f;
float labelRadiusOffset = radius / 10f * 3.6f;
if (mChart.isDrawHoleEnabled()) {
labelRadiusOffset = (radius - (radius * holeRadiusPercent)) / 2f;
+
+ if (!mChart.isDrawSlicesUnderHoleEnabled() && mChart.isDrawRoundedSlicesEnabled()) {
+ // Add curved circle slice and spacing to rotation angle, so that it sits nicely inside
+ rotationAngle += roundedRadius * 360 / (Math.PI * 2 * radius);
+ }
}
final float labelRadius = radius - labelRadiusOffset;
@@ -472,6 +499,7 @@ public class PieChartRenderer extends DataRenderer {
float value = mChart.isUsePercentValuesEnabled() ? entry.getY()
/ yValueSum * 100f : entry.getY();
String formattedValue = formatter.getPieLabel(value, entry);
+ String entryLabel = entry.getLabel();
final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD);
final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD);
@@ -552,13 +580,13 @@ public class PieChartRenderer extends DataRenderer {
drawValue(c, formattedValue, labelPtx, labelPty, dataSet.getValueTextColor(j));
- if (j < data.getEntryCount() && entry.getLabel() != null) {
- drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight);
+ if (j < data.getEntryCount() && entryLabel != null) {
+ drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight);
}
} else if (drawXOutside) {
- if (j < data.getEntryCount() && entry.getLabel() != null) {
- drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight / 2.f);
+ if (j < data.getEntryCount() && entryLabel != null) {
+ drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight / 2.f);
}
} else if (drawYOutside) {
@@ -578,13 +606,13 @@ public class PieChartRenderer extends DataRenderer {
drawValue(c, formattedValue, x, y, dataSet.getValueTextColor(j));
- if (j < data.getEntryCount() && entry.getLabel() != null) {
- drawEntryLabel(c, entry.getLabel(), x, y + lineHeight);
+ if (j < data.getEntryCount() && entryLabel != null) {
+ drawEntryLabel(c, entryLabel, x, y + lineHeight);
}
} else if (drawXInside) {
- if (j < data.getEntryCount() && entry.getLabel() != null) {
- drawEntryLabel(c, entry.getLabel(), x, y + lineHeight / 2f);
+ if (j < data.getEntryCount() && entryLabel != null) {
+ drawEntryLabel(c, entryLabel, x, y + lineHeight / 2f);
}
} else if (drawYInside) {
drawValue(c, formattedValue, x, y + lineHeight / 2f, dataSet.getValueTextColor(j));
@@ -637,7 +665,6 @@ public class PieChartRenderer extends DataRenderer {
@Override
public void drawExtras(Canvas c) {
- // drawCircles(c);
drawHole(c);
c.drawBitmap(mDrawBitmap.get(), 0, 0, null);
drawCenterText(c);
@@ -763,6 +790,15 @@ public class PieChartRenderer extends DataRenderer {
@Override
public void drawHighlighted(Canvas c, Highlight[] indices) {
+ /* Skip entirely if using rounded circle slices, because it doesn't make sense to highlight
+ * in this way.
+ * TODO: add support for changing slice color with highlighting rather than only shifting the slice
+ */
+
+ final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled();
+ if (drawInnerArc && mChart.isDrawRoundedSlicesEnabled())
+ return;
+
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
@@ -773,7 +809,6 @@ public class PieChartRenderer extends DataRenderer {
float[] absoluteAngles = mChart.getAbsoluteAngles();
final MPPointF center = mChart.getCenterCircleBox();
final float radius = mChart.getRadius();
- final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled();
final float userInnerRadius = drawInnerArc
? radius * (mChart.getHoleRadius() / 100.f)
: 0.f;