avram andrei tiberiu
avram andrei tiberiu

Reputation: 115

Pie chart with labels in compose

I'm trying to build a similar chart in Jetpack compose to the one from the screenshot. Let's assume that instead of those labels I'm trying to draw squares ( which should be icons actually but it does not change the problem). I managed to draw all the arches but when I draw the squares they are not possitioned as I would like them to be The issue is that the square is drawn from the top left corner. And it works fine if they are drawn from 0 to 90 degrees (clockwise). But how I can reposition the squares as if from 90 to 180 degrees they are drawn from the top right, from 180 to 270 they are drawn from the bottom right and from 270 to 360 they are drawn from bottom left.

Currently I have the following code for drawing the squares

val angle = spendingChartItem.startAngle + spendingChartItem.archAngle / 2f

        val offset = Offset(
            drawScope.center.x + (circleInnerRadius + chartBarWidth.toPx() / 2) * cos(
                angle.degreeToRadians
            ),
            drawScope.center.y + (circleInnerRadius + chartBarWidth.toPx() / 2) * sin(
                angle.degreeToRadians
            )
        )
        drawRect(
            color = Color.Red,
            size =   androidx.compose.ui.geometry.Size(24f,24f),
            topLeft = offset
        )

enter image description here

Upvotes: 3

Views: 1244

Answers (1)

Thracian
Thracian

Reputation: 67248

You need to take size of rectangle or image into consideration while setting position via angle.

enter image description here

 val rectWidth = 20.dp.toPx()
    drawRect(
        color = Color.Red,
        size = Size(rectWidth, rectWidth),
        topLeft = Offset(
            -rectWidth / 2 + center.x + (radius + strokeWidth) * cos(
                angleInRadians
            ),
            -rectWidth / 2 + center.y + (radius + strokeWidth) * sin(
                angleInRadians
            )
        )
    )

Full sample

@Preview
@Composable
private fun PieChartWithLabels() {

    Box(
        modifier = Modifier
            .fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        val chartDataList = listOf(
            ChartData(Pink400, 10f),
            ChartData(Orange400, 20f),
            ChartData(Yellow400, 15f),
            ChartData(Green400, 5f),
            ChartData(Blue400, 50f),
        )


        Canvas(
            modifier = Modifier
                .fillMaxWidth(.7f)
                .aspectRatio(1f)
        ) {
            val width = size.width
            val radius = width / 2f
            val strokeWidth = 20.dp.toPx()

            var startAngle = -90f

            for (index in 0..chartDataList.lastIndex) {

                val chartData = chartDataList[index]
                val sweepAngle = chartData.data.asAngle
                val angleInRadians = (startAngle + sweepAngle / 2).degreeToAngle


                drawArc(
                    color = chartData.color,
                    startAngle = startAngle,
                    sweepAngle = sweepAngle,
                    useCenter = false,
                    topLeft = Offset(strokeWidth / 2, strokeWidth / 2),
                    size = Size(width - strokeWidth, width - strokeWidth),
                    style = Stroke(strokeWidth)
                )


                val rectWidth = 20.dp.toPx()
                drawRect(
                    color = Color.Red,
                    size = Size(rectWidth, rectWidth),
                    topLeft = Offset(
                        -rectWidth / 2 + center.x + (radius + strokeWidth) * cos(
                            angleInRadians
                        ),
                        -rectWidth / 2 + center.y + (radius + strokeWidth) * sin(
                            angleInRadians
                        )
                    )
                )

                startAngle += sweepAngle
            }
        }
    }
}

private val Float.degreeToAngle
    get() = (this * Math.PI / 180f).toFloat()


@Immutable
data class ChartData(val color: Color, val data: Float)

Upvotes: 1

Related Questions