package com.github.mikephil.charting.renderer;
import android.graphics.Canvas;
import android.graphics.Paint.Align;
import android.graphics.RectF;
import com.github.mikephil.charting.animation.ChartAnimator;
import com.github.mikephil.charting.buffer.BarBuffer;
import com.github.mikephil.charting.buffer.HorizontalBarBuffer;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.formatter.IValueFormatter;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface;
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;
import java.util.List;
/**
* Renderer for the HorizontalBarChart.
*
* @author Philipp Jahoda
*/
public class HorizontalBarChartRenderer extends BarChartRenderer {
public HorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator,
ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
mValuePaint.setTextAlign(Align.LEFT);
}
@Override
public void initBuffers() {
BarData barData = mChart.getBarData();
mBarBuffers = new HorizontalBarBuffer[barData.getDataSetCount()];
for (int i = 0; i < mBarBuffers.length; i++) {
IBarDataSet set = barData.getDataSetByIndex(i);
mBarBuffers[i] = new HorizontalBarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1),
barData.getDataSetCount(), set.isStacked());
}
}
private RectF mBarShadowRectBuffer = new RectF();
@Override
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mBarBorderPaint.setColor(dataSet.getBarBorderColor());
mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth()));
final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f;
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
// draw the bar shadow before the values
if (mChart.isDrawBarShadowEnabled()) {
mShadowPaint.setColor(dataSet.getBarShadowColor());
BarData barData = mChart.getBarData();
final float barWidth = barData.getBarWidth();
final float barWidthHalf = barWidth / 2.0f;
float x;
for (int i = 0, count = Math.min((int)(Math.ceil((float)(dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount());
i < count;
i++) {
BarEntry e = dataSet.getEntryForIndex(i);
x = e.getX();
mBarShadowRectBuffer.top = x - barWidthHalf;
mBarShadowRectBuffer.bottom = x + barWidthHalf;
trans.rectValueToPixel(mBarShadowRectBuffer);
if (!mViewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom))
continue;
if (!mViewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top))
break;
mBarShadowRectBuffer.left = mViewPortHandler.contentLeft();
mBarShadowRectBuffer.right = mViewPortHandler.contentRight();
c.drawRect(mBarShadowRectBuffer, mShadowPaint);
}
}
// initialize the buffer
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);
final boolean isSingleColor = dataSet.getColors().size() == 1;
if (isSingleColor) {
mRenderPaint.setColor(dataSet.getColor());
}
for (int j = 0; j < buffer.size(); j += 4) {
if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3]))
break;
if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
continue;
if (!isSingleColor) {
// Set the color for the currently drawn value. If the index
// is out of bounds, reuse colors.
mRenderPaint.setColor(dataSet.getColor(j / 4));
}
c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3], mRenderPaint);
if (drawBorder) {
c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3], mBarBorderPaint);
}
}
}
@Override
public void drawValues(Canvas c) {
// if values are drawn
if (isDrawingValuesAllowed(mChart)) {
List<IBarDataSet> dataSets = mChart.getBarData().getDataSets();
final float valueOffsetPlus = Utils.convertDpToPixel(5f);
float posOffset = 0f;
float negOffset = 0f;
final boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled();
for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) {
IBarDataSet dataSet = dataSets.get(i);
if (!shouldDrawValues(dataSet))
continue;
boolean isInverted = mChart.isInverted(dataSet.getAxisDependency());
// apply the text-styling defined by the DataSet
applyValueTextStyle(dataSet);
final float halfTextHeight = Utils.calcTextHeight(mValuePaint, "10") / 2f;
IValueFormatter formatter = dataSet.getValueFormatter();
// get the buffer
BarBuffer buffer = mBarBuffers[i];
final float phaseY = mAnimator.getPhaseY();
// if only single values are drawn (sum)
if (!dataSet.isStacked()) {
for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) {
float y = (buffer.buffer[j + 1] + buffer.buffer[j + 3]) / 2f;
if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1]))
break;
if (!mViewPortHandler.isInBoundsX(buffer.buffer[j]))
continue;
if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
continue;
BarEntry e = dataSet.getEntryForIndex(j / 4);
float val = e.getY();
String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler);
// calculate the correct offset depending on the draw position of the value
float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue);
posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus));
negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus);
if (isInverted) {
posOffset = -posOffset - valueTextWidth;
negOffset = -negOffset - valueTextWidth;
}
drawValue(c, formattedValue, buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset),
y + halfTextHeight, dataSet.getValueTextColor(j / 2));
}
// if each value of a potential stack should be drawn
} else {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
int bufferIndex = 0;
int index = 0;
while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) {
BarEntry e = dataSet.getEntryForIndex(index);
int color = dataSet.getValueTextColor(index);
float[] vals = e.getYVals();
// we still draw stacked bars, but there is one
// non-stacked
// in between
if (vals == null) {
if (!mViewPortHandler.isInBoundsTop(buffer.buffer[bufferIndex + 1]))
break;
if (!mViewPortHandler.isInBoundsX(buffer.buffer[bufferIndex]))
continue;
if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1]))
continue;
float val = e.getY();
String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler);
// calculate the correct offset depending on the draw position of the value
float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue);
posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus));
negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus);
if (isInverted) {
posOffset = -posOffset - valueTextWidth;
negOffset = -negOffset - valueTextWidth;
}
drawValue(c, formattedValue, buffer.buffer[bufferIndex + 2]
+ (e.getY() >= 0 ? posOffset : negOffset),
buffer.buffer[bufferIndex + 1] + halfTextHeight, color);
} else {
float[] transformed = new float[vals.length * 2];
float posY = 0f;
float negY = -e.getNegativeSum();
for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) {
float value = vals[idx];
float y;
if (value >= 0f) {
posY += value;
y = posY;
} else {
y = negY;
negY -= value;
}
transformed[k] = y * phaseY;
}
trans.pointValuesToPixel(transformed);
for (int k = 0; k < transformed.length; k += 2) {
float val = vals[k / 2];
String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler);
// calculate the correct offset depending on the draw position of the value
float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue);
posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus));
negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus);
if (isInverted) {
posOffset = -posOffset - valueTextWidth;
negOffset = -negOffset - valueTextWidth;
}
float x = transformed[k]
+ (val >= 0 ? posOffset : negOffset);
float y = (buffer.buffer[bufferIndex + 1] + buffer.buffer[bufferIndex + 3]) / 2f;
if (!mViewPortHandler.isInBoundsTop(y))
break;
if (!mViewPortHandler.isInBoundsX(x))
continue;
if (!mViewPortHandler.isInBoundsBottom(y))
continue;
drawValue(c, formattedValue, x, y + halfTextHeight, color);
}
}
bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length;
index++;
}
}
}
}
}
protected void drawValue(Canvas c, String valueText, float x, float y, int color) {
mValuePaint.setColor(color);
c.drawText(valueText, x, y, mValuePaint);
}
@Override
protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) {
float top = x - barWidthHalf;
float bottom = x + barWidthHalf;
float left = y1;
float right = y2;
mBarRect.set(left, top, right, bottom);
trans.rectToPixelPhaseHorizontal(mBarRect, mAnimator.getPhaseY());
}
@Override
protected void setHighlightDrawPos(Highlight high, RectF bar) {
high.setDraw(bar.centerY(), bar.right);
}
@Override
protected boolean isDrawingValuesAllowed(ChartInterface chart) {
return chart.getData().getEntryCount() < chart.getMaxVisibleCount()
* mViewPortHandler.getScaleY();
}
}