From 98b59c0d28f00416d1e74521122a9d7842fc031d Mon Sep 17 00:00:00 2001 From: Hannes Achleitner Date: Wed, 24 Dec 2025 17:39:17 +0100 Subject: [PATCH] Data nullable --- .../renderer/HorizontalBarChartRenderer.kt | 442 +++++++++--------- 1 file changed, 223 insertions(+), 219 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.kt index f56ced509b..ac5f8cc1f1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.kt +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.kt @@ -27,16 +27,17 @@ open class HorizontalBarChartRenderer( viewPortHandler: ViewPortHandler ) : BarChartRenderer(chart, animator, viewPortHandler) { override fun initBuffers() { - val barData = dataProvider.barData - barBuffers = arrayOfNulls(barData.dataSetCount).toMutableList() - - for (i in barBuffers.indices) { - val set = barData?.getDataSetByIndex(i) - set?.let { - barBuffers[i] = HorizontalBarBuffer( - it.entryCount * 4 * (if (set.isStacked) set.stackSize else 1), - barData.dataSetCount, set.isStacked - ) + dataProvider.barData?.let { barData -> + barBuffers = arrayOfNulls(barData.dataSetCount).toMutableList() + + for (i in barBuffers.indices) { + val set = barData.getDataSetByIndex(i) + set?.let { + barBuffers[i] = HorizontalBarBuffer( + it.entryCount * 4 * (if (set.isStacked) set.stackSize else 1), + barData.dataSetCount, set.isStacked + ) + } } } } @@ -62,40 +63,41 @@ open class HorizontalBarChartRenderer( if (dataProvider.isDrawBarShadowEnabled) { shadowPaint.color = dataSet.barShadowColor - val barData = dataProvider.barData + dataProvider.barData?.let { barData -> - val barWidth = barData.barWidth - val barWidthHalf = barWidth / 2.0f - var x: Float + val barWidth = barData.barWidth + val barWidthHalf = barWidth / 2.0f + var x: Float - var i = 0 - val count = min((ceil(((dataSet.entryCount).toFloat() * phaseX).toDouble())).toInt().toDouble(), dataSet.entryCount.toDouble()).toInt() - while (i < count - ) { - val barEntry = dataSet.getEntryForIndex(i) - barEntry?.let { - x = it.x + var i = 0 + val count = min((ceil(((dataSet.entryCount).toFloat() * phaseX).toDouble())).toInt().toDouble(), dataSet.entryCount.toDouble()).toInt() + while (i < count + ) { + val barEntry = dataSet.getEntryForIndex(i) + barEntry?.let { + x = it.x - mBarShadowRectBuffer.top = x - barWidthHalf - mBarShadowRectBuffer.bottom = x + barWidthHalf + mBarShadowRectBuffer.top = x - barWidthHalf + mBarShadowRectBuffer.bottom = x + barWidthHalf - trans!!.rectValueToPixel(mBarShadowRectBuffer) + trans!!.rectValueToPixel(mBarShadowRectBuffer) - if (!viewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom)) { - i++ - continue - } + if (!viewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom)) { + i++ + continue + } - if (!viewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top)) { - break - } + if (!viewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top)) { + break + } - mBarShadowRectBuffer.left = viewPortHandler.contentLeft() - mBarShadowRectBuffer.right = viewPortHandler.contentRight() + mBarShadowRectBuffer.left = viewPortHandler.contentLeft() + mBarShadowRectBuffer.right = viewPortHandler.contentRight() - canvas.drawRect(mBarShadowRectBuffer, shadowPaint) + canvas.drawRect(mBarShadowRectBuffer, shadowPaint) + } + i++ } - i++ } } @@ -169,144 +171,69 @@ open class HorizontalBarChartRenderer( override fun drawValues(canvas: Canvas) { // if values are drawn if (isDrawingValuesAllowed(dataProvider)) { - val dataSets = dataProvider.barData.dataSets + val dataSets = dataProvider.barData?.dataSets val valueOffsetPlus = 5f.convertDpToPixel() var posOffset: Float var negOffset: Float val drawValueAboveBar = dataProvider.isDrawValueAboveBarEnabled - for (i in 0.. + for (i in 0..= 0) posOffset else negOffset), - y + halfTextHeight, - dataSet.getValueTextColor(j / 2) - ) + if (!viewPortHandler.isInBoundsX(buffer.buffer[j])) { + j += 4 + continue } - if (barEntry.icon != null && dataSet.isDrawIcons) { - val icon = barEntry.icon - - var px = buffer.buffer[j + 2] + (if (valueY >= 0) posOffset else negOffset) - var py = y - - px += iconsOffset.x - py += iconsOffset.y - - icon?.let { - Utils.drawImage( - canvas, - it, - px.toInt(), - py.toInt() - ) - } + if (!viewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) { + j += 4 + continue } - } - j += 4 - } - - // if each value of a potential stack should be drawn - } else { - val trans = dataProvider.getTransformer(dataSet.axisDependency) - - var bufferIndex = 0 - var index = 0 - - while (index < dataSet.entryCount * animator.phaseX) { - val barEntry = dataSet.getEntryForIndex(index) - barEntry?.let { - val color = dataSet.getValueTextColor(index) - val vals = it.yVals - - // we still draw stacked bars, but there is one - // non-stacked - // in between - if (vals == null) { - if (!viewPortHandler.isInBoundsTop(buffer.buffer[bufferIndex + 1])) { - break - } - - if (!viewPortHandler.isInBoundsX(buffer.buffer[bufferIndex])) { - continue - } - - if (!viewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1])) { - continue - } - - val formattedValue = formatter.getFormattedValue( - it.y, - it, i, viewPortHandler - ) + val barEntry = dataSet.getEntryForIndex(j / 4) + barEntry?.let { + val valueY = barEntry.y + val formattedValue = formatter.getFormattedValue(valueY, barEntry, i, viewPortHandler) // calculate the correct offset depending on the draw position of the value val valueTextWidth = Utils.calcTextWidth(paintValues, formattedValue).toFloat() posOffset = (if (drawValueAboveBar) valueOffsetPlus else -(valueTextWidth + valueOffsetPlus)) - negOffset = (if (drawValueAboveBar) -(valueTextWidth + valueOffsetPlus) else valueOffsetPlus) + negOffset = ((if (drawValueAboveBar) -(valueTextWidth + valueOffsetPlus) else valueOffsetPlus) + - (buffer.buffer[j + 2] - buffer.buffer[j])) if (isInverted) { posOffset = -posOffset - valueTextWidth @@ -315,69 +242,67 @@ open class HorizontalBarChartRenderer( if (dataSet.isDrawValues) { drawValue( - canvas, formattedValue!!, - buffer.buffer[bufferIndex + 2] - + (if (it.y >= 0) posOffset else negOffset), - buffer.buffer[bufferIndex + 1] + halfTextHeight, color + canvas, + formattedValue!!, + buffer.buffer[j + 2] + (if (valueY >= 0) posOffset else negOffset), + y + halfTextHeight, + dataSet.getValueTextColor(j / 2) ) } - if (it.icon != null && dataSet.isDrawIcons) { - val icon = it.icon + if (barEntry.icon != null && dataSet.isDrawIcons) { + val icon = barEntry.icon - var px = (buffer.buffer[bufferIndex + 2] - + (if (it.y >= 0) posOffset else negOffset)) - var py = buffer.buffer[bufferIndex + 1] + var px = buffer.buffer[j + 2] + (if (valueY >= 0) posOffset else negOffset) + var py = y px += iconsOffset.x py += iconsOffset.y - icon?.let { myIcon -> + icon?.let { Utils.drawImage( canvas, - myIcon, + it, px.toInt(), py.toInt() ) } } - } else { - val transformed = FloatArray(vals.size * 2) + } + j += 4 + } - var posY = 0f - var negY = -it.negativeSum + // if each value of a potential stack should be drawn + } else { + val trans = dataProvider.getTransformer(dataSet.axisDependency) - run { - var k = 0 - var idx = 0 - while (k < transformed.size) { - val value = vals[idx] - val y: Float - - if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) { - // Take care of the situation of a 0.0 value, which overlaps a non-zero bar - y = value - } else if (value >= 0.0f) { - posY += value - y = posY - } else { - y = negY - negY -= value - } + var bufferIndex = 0 + var index = 0 - transformed[k] = y * phaseY - k += 2 - idx++ + while (index < dataSet.entryCount * animator.phaseX) { + val barEntry = dataSet.getEntryForIndex(index) + barEntry?.let { + val color = dataSet.getValueTextColor(index) + val vals = it.yVals + + // we still draw stacked bars, but there is one + // non-stacked + // in between + if (vals == null) { + if (!viewPortHandler.isInBoundsTop(buffer.buffer[bufferIndex + 1])) { + break } - } - trans!!.pointValuesToPixel(transformed) + if (!viewPortHandler.isInBoundsX(buffer.buffer[bufferIndex])) { + continue + } + + if (!viewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1])) { + continue + } - var k = 0 - while (k < transformed.size) { - val valueY = vals[k / 2] val formattedValue = formatter.getFormattedValue( - valueY, + it.y, it, i, viewPortHandler ) @@ -391,52 +316,131 @@ open class HorizontalBarChartRenderer( negOffset = -negOffset - valueTextWidth } - val drawBelow = (valueY == 0.0f && negY == 0.0f && posY > 0.0f) || valueY < 0.0f - - val x = (transformed[k] + (if (drawBelow) negOffset else posOffset)) - val y = (buffer.buffer[bufferIndex + 1] + buffer.buffer[bufferIndex + 3]) / 2f - - if (!viewPortHandler.isInBoundsTop(y)) { - break - } - - if (!viewPortHandler.isInBoundsX(x)) { - k += 2 - continue - } - - if (!viewPortHandler.isInBoundsBottom(y)) { - k += 2 - continue - } - if (dataSet.isDrawValues) { - drawValue(canvas, formattedValue!!, x, y + halfTextHeight, color) + drawValue( + canvas, formattedValue!!, + buffer.buffer[bufferIndex + 2] + + (if (it.y >= 0) posOffset else negOffset), + buffer.buffer[bufferIndex + 1] + halfTextHeight, color + ) } if (it.icon != null && dataSet.isDrawIcons) { val icon = it.icon + var px = (buffer.buffer[bufferIndex + 2] + + (if (it.y >= 0) posOffset else negOffset)) + var py = buffer.buffer[bufferIndex + 1] + + px += iconsOffset.x + py += iconsOffset.y + icon?.let { myIcon -> Utils.drawImage( canvas, myIcon, - (x + iconsOffset.x).toInt(), - (y + iconsOffset.y).toInt() + px.toInt(), + py.toInt() ) } } - k += 2 + } else { + val transformed = FloatArray(vals.size * 2) + + var posY = 0f + var negY = -it.negativeSum + + run { + var k = 0 + var idx = 0 + while (k < transformed.size) { + val value = vals[idx] + val y: Float + + if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) { + // Take care of the situation of a 0.0 value, which overlaps a non-zero bar + y = value + } else if (value >= 0.0f) { + posY += value + y = posY + } else { + y = negY + negY -= value + } + + transformed[k] = y * phaseY + k += 2 + idx++ + } + } + + trans!!.pointValuesToPixel(transformed) + + var k = 0 + while (k < transformed.size) { + val valueY = vals[k / 2] + val formattedValue = formatter.getFormattedValue( + valueY, + it, i, viewPortHandler + ) + + // calculate the correct offset depending on the draw position of the value + val valueTextWidth = Utils.calcTextWidth(paintValues, formattedValue).toFloat() + posOffset = (if (drawValueAboveBar) valueOffsetPlus else -(valueTextWidth + valueOffsetPlus)) + negOffset = (if (drawValueAboveBar) -(valueTextWidth + valueOffsetPlus) else valueOffsetPlus) + + if (isInverted) { + posOffset = -posOffset - valueTextWidth + negOffset = -negOffset - valueTextWidth + } + + val drawBelow = (valueY == 0.0f && negY == 0.0f && posY > 0.0f) || valueY < 0.0f + + val x = (transformed[k] + (if (drawBelow) negOffset else posOffset)) + val y = (buffer.buffer[bufferIndex + 1] + buffer.buffer[bufferIndex + 3]) / 2f + + if (!viewPortHandler.isInBoundsTop(y)) { + break + } + + if (!viewPortHandler.isInBoundsX(x)) { + k += 2 + continue + } + + if (!viewPortHandler.isInBoundsBottom(y)) { + k += 2 + continue + } + + if (dataSet.isDrawValues) { + drawValue(canvas, formattedValue!!, x, y + halfTextHeight, color) + } + + if (it.icon != null && dataSet.isDrawIcons) { + val icon = it.icon + + icon?.let { myIcon -> + Utils.drawImage( + canvas, + myIcon, + (x + iconsOffset.x).toInt(), + (y + iconsOffset.y).toInt() + ) + } + } + k += 2 + } } - } - bufferIndex = if (vals == null) bufferIndex + 4 else bufferIndex + 4 * vals.size + bufferIndex = if (vals == null) bufferIndex + 4 else bufferIndex + 4 * vals.size + } + index++ } - index++ } - } - MPPointF.recycleInstance(iconsOffset) + MPPointF.recycleInstance(iconsOffset) + } } } }