From 83a9e2d62e4851b61f153d6470a27481d74ea023 Mon Sep 17 00:00:00 2001 From: Mohini Katara Date: Wed, 4 Oct 2023 17:50:32 +0530 Subject: [PATCH] Added RoundedBarChartRenderer & RoundedHorizontalBarChartRenderer for corner radius positive/negative Bar Chart --- .../renderer/RoundedBarChartRenderer.java | 324 ++++++++++++++++++ .../RoundedHorizontalBarChartRenderer.java | 242 +++++++++++++ 2 files changed, 566 insertions(+) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java new file mode 100644 index 0000000000..86d3ce4799 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java @@ -0,0 +1,324 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.Shader; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.buffer.BarBuffer; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.Range; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +public class RoundedBarChartRenderer extends BarChartRenderer { + + public RoundedBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(chart, animator, viewPortHandler); + } + private final RectF mBarShadowRectBuffer = new RectF(); + private float mRadius = 20f; + private float roundedShadowRadius = 0f; + private float roundedPositiveDataSetRadius = 0f; + private float roundedNegativeDataSetRadius = 0f; + public void setRoundedNegativeDataSetRadius(float roundedNegativeDataSet) { + roundedNegativeDataSetRadius = roundedNegativeDataSet; + } + public void setRoundedShadowRadius(float roundedShadow) { + roundedShadowRadius = roundedShadow; + } + public void setRoundedPositiveDataSetRadius(float roundedPositiveDataSet) { + roundedPositiveDataSetRadius = roundedPositiveDataSet; + } + + @Override + protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { + initBuffers(); + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + mBarBorderPaint.setColor(dataSet.getBarBorderColor()); + mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); + mShadowPaint.setColor(dataSet.getBarShadowColor()); + boolean drawBorder = dataSet.getBarBorderWidth() > 0f; + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + if (mChart.isDrawBarShadowEnabled()) { + mShadowPaint.setColor(dataSet.getBarShadowColor()); + BarData barData = mChart.getBarData(); + float barWidth = barData.getBarWidth(); + float barWidthHalf = barWidth / 2.0f; + float x; + int i = 0; + double count = Math.min(Math.ceil((int) (double) ((float) dataSet.getEntryCount() * phaseX)), dataSet.getEntryCount()); + while (i < count) { + BarEntry e = dataSet.getEntryForIndex(i); + x = e.getX(); + mBarShadowRectBuffer.left = x - barWidthHalf; + mBarShadowRectBuffer.right = x + barWidthHalf; + trans.rectValueToPixel(mBarShadowRectBuffer); + if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right)) { + i++; + continue; + } + if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left)) + break; + mBarShadowRectBuffer.top = mViewPortHandler.contentTop(); + mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom(); + + + if (roundedShadowRadius >0) { + c.drawRoundRect(mBarRect, roundedShadowRadius, roundedShadowRadius, mShadowPaint); + } else { + c.drawRect(mBarShadowRectBuffer, mShadowPaint); + } + i++; + } + } + + BarBuffer buffer = mBarBuffers[index]; + buffer.setPhases(phaseX, phaseY); + buffer.setDataSet(index); + buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); + buffer.setBarWidth(mChart.getBarData().getBarWidth()); + buffer.feed(dataSet); + trans.pointValuesToPixel(buffer.buffer); + + // if multiple colors has been assigned to Bar Chart + if (dataSet.getColors().size() > 1) { + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) + continue; + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + break; + + if (mChart.isDrawBarShadowEnabled()) { + if (roundedShadowRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); + else + c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom(), mShadowPaint); + } + + // Set the color for the currently drawn value. If the index + mRenderPaint.setColor(dataSet.getColor(j / 4)); + + if (roundedPositiveDataSetRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + } else { + + mRenderPaint.setColor(dataSet.getColor()); + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) + continue; + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + break; + + if (mChart.isDrawBarShadowEnabled()) { + if (roundedShadowRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + + if (roundedPositiveDataSetRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + } + + + boolean isSingleColor = dataSet.getColors().size() == 1; + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor(index)); + } + + int j = 0; + while (j < buffer.size()) { + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) { + j += 4; + continue; + } + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + break; + + if (!isSingleColor) { + mRenderPaint.setColor(dataSet.getColor(j/4)); + } + + mRenderPaint.setShader(new LinearGradient( + buffer.buffer[j], + buffer.buffer[j + 3], + buffer.buffer[j], + buffer.buffer[j + 1], + dataSet.getColor(j/4), + dataSet.getColor(j/4), + Shader.TileMode.MIRROR)); + + mRenderPaint.setShader(new LinearGradient( + buffer.buffer[j], + buffer.buffer[j + 3], + buffer.buffer[j], + buffer.buffer[j + 1], + dataSet.getColor(j/4), + dataSet.getColor(j/4), + Shader.TileMode.MIRROR)); + + + if((dataSet.getEntryForIndex(j/4).getY()<0 && roundedNegativeDataSetRadius >0)) { + Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedNegativeDataSetRadius, roundedNegativeDataSetRadius, true, true, true, true); + c.drawPath(path2, mRenderPaint); + } else if((dataSet.getEntryForIndex(j/4).getY()>0 && roundedPositiveDataSetRadius >0)){ + Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, true, true, true, true); + c.drawPath(path2, mRenderPaint); + } + else { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + + j += 4; + } + + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + BarData barData = mChart.getBarData(); + + for (Highlight high : indices) { + + IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) { + continue; + } + + BarEntry e = set.getEntryForXValue(high.getX(), high.getY()); + + if (!isInBoundsX(e, set)) { + continue; + } + + Transformer trans = mChart.getTransformer(set.getAxisDependency()); + + mHighlightPaint.setColor(set.getHighLightColor()); + mHighlightPaint.setAlpha(set.getHighLightAlpha()); + + boolean isStack = high.getStackIndex() >= 0 && e.isStacked(); + + final float y1; + final float y2; + + if (isStack) { + + if (mChart.isHighlightFullBarEnabled()) { + + y1 = e.getPositiveSum(); + y2 = -e.getNegativeSum(); + + } else { + + Range range = e.getRanges()[high.getStackIndex()]; + + y1 = range.from; + y2 = range.to; + } + + } else { + y1 = e.getY(); + y2 = 0.f; + } + + prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans); + + setHighlightDrawPos(high, mBarRect); + + Path path2 = roundRect(new RectF(mBarRect.left, mBarRect.top, mBarRect.right, + mBarRect.bottom), mRadius, mRadius, true, true, true, true); + + c.drawPath(path2, mHighlightPaint); + } + } + + private Path roundRect(RectF rect, float rx, float ry, boolean tl, boolean tr, boolean br, boolean bl) { + float top = rect.top; + float left = rect.left; + float right = rect.right; + float bottom = rect.bottom; + Path path = new Path(); + if (rx < 0) rx = 0; + if (ry < 0) ry = 0; + float width = right - left; + float height = bottom - top; + if (rx > width / 2) rx = width / 2; + if (ry > height / 2) ry = height / 2; + float widthMinusCorners = (width - (2 * rx)); + float heightMinusCorners = (height - (2 * ry)); + + path.moveTo(right, top + ry); + if (tr) + path.rQuadTo(0, -ry, -rx, -ry);//top-right corner + else { + path.rLineTo(0, -ry); + path.rLineTo(-rx, 0); + } + path.rLineTo(-widthMinusCorners, 0); + if (tl) + path.rQuadTo(-rx, 0, -rx, ry); //top-left corner + else { + path.rLineTo(-rx, 0); + path.rLineTo(0, ry); + } + path.rLineTo(0, heightMinusCorners); + + if (bl) + path.rQuadTo(0, ry, rx, ry);//bottom-left corner + else { + path.rLineTo(0, ry); + path.rLineTo(rx, 0); + } + + path.rLineTo(widthMinusCorners, 0); + if (br) + path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner + else { + path.rLineTo(rx, 0); + path.rLineTo(0, -ry); + } + + path.rLineTo(0, -heightMinusCorners); + path.close(); + return path; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java new file mode 100644 index 0000000000..9d14a321ba --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java @@ -0,0 +1,242 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.buffer.BarBuffer; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +public class RoundedHorizontalBarChartRenderer extends HorizontalBarChartRenderer { + + private final RectF mBarShadowRectBuffer = new RectF(); + private float roundedShadowRadius = 0f; + private float roundedPositiveDataSetRadius = 0f; + private float roundedNegativeDataSetRadius = 0f; + public void setRoundedNegativeDataSetRadius(float roundedNegativeDataSet) { + roundedNegativeDataSetRadius = roundedNegativeDataSet; + } + public void setRoundedShadowRadius(float roundedShadow) { + roundedShadowRadius = roundedShadow; + } + public void setRoundedPositiveDataSetRadius(float roundedPositiveDataSet) { + roundedPositiveDataSetRadius = roundedPositiveDataSet; + } + public RoundedHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(chart, animator, viewPortHandler); + + mValuePaint.setTextAlign(Paint.Align.LEFT); + } + + @Override + protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { + initBuffers(); + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + mBarBorderPaint.setColor(dataSet.getBarBorderColor()); + mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); + mShadowPaint.setColor(dataSet.getBarShadowColor()); + boolean drawBorder = dataSet.getBarBorderWidth() > 0f; + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + if (mChart.isDrawBarShadowEnabled()) { + mShadowPaint.setColor(dataSet.getBarShadowColor()); + BarData barData = mChart.getBarData(); + float barWidth = barData.getBarWidth(); + float barWidthHalf = barWidth / 2.0f; + float x; + int i = 0; + double count = Math.min(Math.ceil((int) (double) ((float) dataSet.getEntryCount() * phaseX)), dataSet.getEntryCount()); + while (i < count) { + BarEntry e = dataSet.getEntryForIndex(i); + x = e.getX(); + mBarShadowRectBuffer.top = x - barWidthHalf; + mBarShadowRectBuffer.bottom = x + barWidthHalf; + trans.rectValueToPixel(mBarShadowRectBuffer); + if (!mViewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom)) { + i++; + continue; + } + if (!mViewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top)) + break; + mBarShadowRectBuffer.left = mViewPortHandler.contentLeft(); + mBarShadowRectBuffer.right = mViewPortHandler.contentRight(); + + + if (roundedShadowRadius >0) { + c.drawRoundRect(mBarRect, roundedShadowRadius, roundedShadowRadius, mShadowPaint); + } else { + c.drawRect(mBarShadowRectBuffer, mShadowPaint); + } + i++; + } + } + + BarBuffer buffer = mBarBuffers[index]; + buffer.setPhases(phaseX, phaseY); + buffer.setDataSet(index); + buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); + buffer.setBarWidth(mChart.getBarData().getBarWidth()); + buffer.feed(dataSet); + trans.pointValuesToPixel(buffer.buffer); + + // if multiple colors has been assigned to Bar Chart + if (dataSet.getColors().size() > 1) { + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) + continue; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j+1])) + break; + + if (mChart.isDrawBarShadowEnabled()) { + if (roundedShadowRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); + else + c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom(), mShadowPaint); + } + + // Set the color for the currently drawn value. If the index + mRenderPaint.setColor(dataSet.getColor(j / 4)); + + if (roundedPositiveDataSetRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + } else { + + mRenderPaint.setColor(dataSet.getColor()); + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) + continue; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j+1])) + break; + + if (mChart.isDrawBarShadowEnabled()) { + if (roundedShadowRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + + if (roundedPositiveDataSetRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + } + + + boolean isSingleColor = dataSet.getColors().size() == 1; + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor(index)); + } + + int j = 0; + while (j < buffer.size()) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) { + j += 4; + continue; + } + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j+1])) + break; + + if (!isSingleColor) { + mRenderPaint.setColor(dataSet.getColor(j/4)); + } + + if((dataSet.getEntryForIndex(j/4).getY()<0 && roundedNegativeDataSetRadius >0)) { + Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedNegativeDataSetRadius, roundedNegativeDataSetRadius, true, true, true, true); + c.drawPath(path2, mRenderPaint); + } else if((dataSet.getEntryForIndex(j/4).getY()>0 && roundedPositiveDataSetRadius >0)){ + Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, true, true, true, true); + c.drawPath(path2, mRenderPaint); + } + else { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + j += 4; + } + } + + private Path roundRect(RectF rect, float rx, float ry, boolean tl, boolean tr, boolean br, boolean bl) { + float top = rect.top; + float left = rect.left; + float right = rect.right; + float bottom = rect.bottom; + Path path = new Path(); + if (rx < 0) rx = 0; + if (ry < 0) ry = 0; + float width = right - left; + float height = bottom - top; + if (rx > width / 2) rx = width / 2; + if (ry > height / 2) ry = height / 2; + float widthMinusCorners = (width - (2 * rx)); + float heightMinusCorners = (height - (2 * ry)); + + path.moveTo(right, top + ry); + if (tr) + path.rQuadTo(0, -ry, -rx, -ry);//top-right corner + else { + path.rLineTo(0, -ry); + path.rLineTo(-rx, 0); + } + path.rLineTo(-widthMinusCorners, 0); + if (tl) + path.rQuadTo(-rx, 0, -rx, ry); //top-left corner + else { + path.rLineTo(-rx, 0); + path.rLineTo(0, ry); + } + path.rLineTo(0, heightMinusCorners); + + if (bl) + path.rQuadTo(0, ry, rx, ry);//bottom-left corner + else { + path.rLineTo(0, ry); + path.rLineTo(rx, 0); + } + + path.rLineTo(widthMinusCorners, 0); + if (br) + path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner + else { + path.rLineTo(rx, 0); + path.rLineTo(0, -ry); + } + + path.rLineTo(0, -heightMinusCorners); + path.close(); + return path; + } +}