commonMain.com.aay.compose.donutChart.PieChart.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of chart Show documentation
Show all versions of chart Show documentation
Multiplatform library for desktop and android
The newest version!
package com.aay.compose.donutChart
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.TweenSpec
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aay.compose.donutChart.component.PieChartDescriptionComposable
import com.aay.compose.donutChart.component.draPieCircle
import com.aay.compose.donutChart.component.drawPedigreeChart
import com.aay.compose.donutChart.model.ChartTypes
import com.aay.compose.donutChart.model.PieChartData
import com.aay.compose.utils.checkIfDataIsNegative
import kotlin.math.min
import androidx.compose.animation.core.*
import androidx.compose.ui.text.*
import com.aay.compose.baseComponents.model.LegendPosition
import com.aay.compose.utils.ChartDefaultValues
/**
* Composable function to render a pie chart with an optional legend.
*
* @param modifier Modifier for configuring the layout and appearance of the pie chart.
* @param pieChartData List of data for the pie chart, including labels and values.
* @param animation Animation specification for the pie chart transitions (default is a 3-second linear animation).
* @param textRatioStyle TextStyle for configuring the appearance of ratio text labels (default font size is 12sp).
* @param outerCircularColor Color of the outer circular border of the pie chart (default is Gray).
* @param ratioLineColor Color of the lines connecting ratio labels to chart segments (default is Gray).
* @param descriptionStyle TextStyle for configuring the appearance of the chart description (legend) text.
* @param legendPosition Position of the legend within the chart (default is [LegendPosition.TOP]).
*
* @see PieChartData
* @see LegendPosition
*/
@OptIn(ExperimentalTextApi::class)
@Composable
fun PieChart(
modifier: Modifier = Modifier,
pieChartData: List,
animation: AnimationSpec = TweenSpec(durationMillis = 3000),
textRatioStyle: TextStyle = TextStyle.Default.copy(fontSize = 12.sp),
outerCircularColor: Color = Color.Gray,
ratioLineColor: Color = Color.Gray,
descriptionStyle: TextStyle = TextStyle.Default,
legendPosition: LegendPosition = ChartDefaultValues.legendPosition,
) {
var totalSum = 0.0f
val pieValueWithRatio = mutableListOf()
pieChartData.forEach {
totalSum += it.data.toFloat()
}
pieChartData.forEachIndexed { index, part ->
pieValueWithRatio.add(index, 360 * part.data.toFloat() / totalSum)
}
val textMeasure = rememberTextMeasurer()
checkIfDataIsNegative(data = pieChartData.map { it.data })
val transitionProgress = remember(pieValueWithRatio) { Animatable(initialValue = 0F) }
LaunchedEffect(pieChartData) {
transitionProgress.animateTo(1F, animationSpec = animation)
}
Column(
modifier = modifier,
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.Start
) {
when (legendPosition) {
LegendPosition.TOP -> {
PieChartDescriptionComposable(
pieChartData = pieChartData,
descriptionStyle = descriptionStyle,
modifier = Modifier.fillMaxWidth().weight(0.5f)
)
drawPieChart(
modifier = Modifier.weight(1.5f),
pieChartData = pieChartData,
textRatioStyle = textRatioStyle,
outerCircularColor = outerCircularColor,
ratioLineColor = ratioLineColor,
pieValueWithRatio = pieValueWithRatio,
totalSum = totalSum,
transitionProgress = transitionProgress,
textMeasure = textMeasure
)
}
LegendPosition.BOTTOM -> {
drawPieChart(
modifier = Modifier.weight(1.5f),
pieChartData = pieChartData,
textRatioStyle = textRatioStyle,
outerCircularColor = outerCircularColor,
ratioLineColor = ratioLineColor,
pieValueWithRatio = pieValueWithRatio,
totalSum = totalSum,
transitionProgress = transitionProgress,
textMeasure = textMeasure
)
PieChartDescriptionComposable(
pieChartData = pieChartData,
descriptionStyle = descriptionStyle,
modifier = Modifier.fillMaxWidth().weight(0.5f)
)
}
else -> {
drawPieChart(
modifier = Modifier.weight(1.5f),
pieChartData = pieChartData,
textRatioStyle = textRatioStyle,
outerCircularColor = outerCircularColor,
ratioLineColor = ratioLineColor,
pieValueWithRatio = pieValueWithRatio,
totalSum = totalSum,
transitionProgress = transitionProgress,
textMeasure = textMeasure
)
}
}
}
}
@OptIn(ExperimentalTextApi::class)
@Composable
private fun drawPieChart(
modifier: Modifier = Modifier,
pieChartData: List,
textRatioStyle: TextStyle,
outerCircularColor: Color,
ratioLineColor: Color,
pieValueWithRatio: MutableList,
totalSum: Float,
transitionProgress: Animatable,
textMeasure: TextMeasurer,
) {
Box(
modifier = modifier.fillMaxSize()
.drawBehind {
val canvasWidth = size.width
val canvasHeight = size.height
val minValue = min(canvasWidth, canvasHeight)
.coerceAtMost(canvasHeight / 2)
.coerceAtMost(canvasWidth / 2)
val arcWidth =
(size.minDimension.dp.toPx() * 0.13f).coerceAtMost(minValue / 4)
drawPedigreeChart(
pieValueWithRatio = pieValueWithRatio,
pieChartData = pieChartData,
totalSum = totalSum,
transitionProgress = transitionProgress,
textMeasure = textMeasure,
textRatioStyle = textRatioStyle,
ratioLineColor = ratioLineColor,
arcWidth = arcWidth,
minValue = minValue,
pieChart = ChartTypes.PIE_CHART
)
//draw outer circle
draPieCircle(
circleColor = outerCircularColor,
radiusRatioCircle = (minValue / 2) + (arcWidth / 1.5f)
)
})
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy