Skip to content

Commit

Permalink
Add support for pie chart
Browse files Browse the repository at this point in the history
  • Loading branch information
mfracx committed Aug 4, 2023
1 parent d4d8f8a commit 0dfcec4
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 3 deletions.
2 changes: 1 addition & 1 deletion JetChart/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ afterEvaluate {
from components.release
groupId = 'io.jetchart'
artifactId = 'JetChart'
version = '1.1.0'
version = '1.2.0'
}
}
}
Expand Down
74 changes: 74 additions & 0 deletions JetChart/src/main/java/io/jetchart/pie/PieChart.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.jetchart.pie

import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import io.jetchart.common.animation.fadeInAnimation
import io.jetchart.pie.renderer.FilledSliceDrawer
import io.jetchart.pie.renderer.SliceDrawer

@Composable
fun PieChart(
pies: Pies,
modifier: Modifier = Modifier,
animation: AnimationSpec<Float> = fadeInAnimation(),
sliceDrawer: SliceDrawer = FilledSliceDrawer()
) {
val transitionProgress = remember(pies.slices) { Animatable(initialValue = 0f) }

LaunchedEffect(pies.slices) {
transitionProgress.animateTo(1f, animationSpec = animation)
}

DrawChart(
pies = pies,
modifier = modifier.fillMaxSize(),
progress = transitionProgress.value,
sliceDrawer = sliceDrawer
)
}

@Composable
private fun DrawChart(
pies: Pies,
modifier: Modifier,
progress: Float,
sliceDrawer: SliceDrawer
) {
val slices = pies.slices

Canvas(modifier = modifier) {
drawIntoCanvas {
var startArc = 0f

slices.forEach { slice ->
val arc = angleOf(
value = slice.value,
totalValue = pies.totalSize(),
progress = progress
)

sliceDrawer.drawSlice(
drawScope = this,
canvas = drawContext.canvas,
area = size,
startAngle = startArc,
sweepAngle = arc,
slice = slice
)

startArc += arc
}
}
}
}

fun angleOf(value: Float, totalValue: Float, progress: Float): Float {
return 360f * (value * progress) / totalValue
}
5 changes: 5 additions & 0 deletions JetChart/src/main/java/io/jetchart/pie/Pies.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.jetchart.pie

data class Pies(val slices: List<Slice>) {
fun totalSize(): Float = slices.map { it.value }.sum()
}
5 changes: 5 additions & 0 deletions JetChart/src/main/java/io/jetchart/pie/Slice.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.jetchart.pie

import androidx.compose.ui.graphics.Color

data class Slice(val value: Float, val color: Color)
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.jetchart.pie.renderer

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.drawscope.DrawScope
import io.jetchart.pie.Slice

class FilledSliceDrawer(private val thickness: Float = 30f) : SliceDrawer {

private val sectionPaint = Paint().apply {
isAntiAlias = true
style = PaintingStyle.Stroke
}

override fun drawSlice(
drawScope: DrawScope,
canvas: Canvas,
area: Size,
startAngle: Float,
sweepAngle: Float,
slice: Slice
) {
val sliceThickness = calculateSectorThickness(area = area)
val drawableArea = calculateDrawableArea(area = area)

canvas.drawArc(
rect = drawableArea,
paint = sectionPaint.apply {
color = slice.color
strokeWidth = sliceThickness
},
startAngle = startAngle,
sweepAngle = sweepAngle,
useCenter = false
)
}

private fun calculateSectorThickness(area: Size): Float {
val minSize = minOf(area.width, area.height)

return minSize * (thickness / 200f)
}

private fun calculateDrawableArea(area: Size): Rect {
val sliceThicknessOffset =
calculateSectorThickness(area = area) / 2f
val offsetHorizontally = (area.width - area.height) / 2f

return Rect(
left = sliceThicknessOffset + offsetHorizontally,
top = sliceThicknessOffset,
right = area.width - sliceThicknessOffset - offsetHorizontally,
bottom = area.height - sliceThicknessOffset
)
}
}
17 changes: 17 additions & 0 deletions JetChart/src/main/java/io/jetchart/pie/renderer/SliceDrawer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.jetchart.pie.renderer

import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope
import io.jetchart.pie.Slice

interface SliceDrawer {
fun drawSlice(
drawScope: DrawScope,
canvas: Canvas,
area: Size,
startAngle: Float,
sweepAngle: Float,
slice: Slice
)
}
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ allprojects {
2. Add the dependency
```groovy
dependencies {
implementation 'com.github.fracassi-marco:JetChart:1.1.0'
implementation 'com.github.fracassi-marco:JetChart:1.2.0'
}
```

Expand Down Expand Up @@ -115,4 +115,23 @@ fun LineChartComposable() {

@Composable
private fun points(count: Int) = (1..count).map { Point(Random.nextFloat(), "Point$it") }
```

## Pie Chart
![Pie Chart big](docs/pie1.gif) ![Pie Chart small](docs/pie2.gif)

### Features
You can:
- animate the drawing

### Example
```kotlin
@Composable
fun PieChartComposable() {
PieChart(pies = Pies(listOf(Slice(35f, Red), Slice(45f, JetGreen), Slice(15f, Yellow), Slice(5f, Cyan))),
modifier = Modifier.height(340.dp),
animation = fadeInAnimation(4000),
sliceDrawer = FilledSliceDrawer(thickness = 60f)
)
}
```
19 changes: 18 additions & 1 deletion app/src/main/java/io/jetchart/demo/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color.Companion.Blue
import androidx.compose.ui.graphics.Color.Companion.Cyan
import androidx.compose.ui.graphics.Color.Companion.Red
import androidx.compose.ui.graphics.Color.Companion.Transparent
import androidx.compose.ui.graphics.Color.Companion.Yellow
import androidx.compose.ui.unit.dp
import io.jetchart.bar.Bar
import io.jetchart.bar.BarChart
Expand All @@ -43,6 +45,10 @@ import io.jetchart.line.renderer.line.SolidLineDrawer
import io.jetchart.line.renderer.point.FilledPointDrawer
import io.jetchart.line.renderer.xaxis.LineXAxisDrawer
import io.jetchart.line.renderer.yaxis.LineYAxisWithValueDrawer
import io.jetchart.pie.PieChart
import io.jetchart.pie.Pies
import io.jetchart.pie.Slice
import io.jetchart.pie.renderer.FilledSliceDrawer
import kotlin.random.Random

class MainActivity : ComponentActivity() {
Expand All @@ -58,6 +64,8 @@ class MainActivity : ComponentActivity() {
JetDivider()
LineChartComposable()
JetDivider()
PieChartComposable()
JetDivider()
}
}
}
Expand All @@ -71,7 +79,7 @@ fun BarChartComposable(text: MutableState<String>) {
val width = numberOfBars * 80
BarChart(chars = Bars(
bars = (1..numberOfBars).map {
Bar(label = "BAR$it", value = Random.nextFloat(), color = JetGreen) {
Bar(label = "BAR$it", value = Random.nextFloat(), color = if(it % 2 == 0) JetGreen else Red) {
bar -> text.value = "You clicked on the bar ${bar.label}!"
}
}),
Expand Down Expand Up @@ -105,6 +113,15 @@ fun LineChartComposable() {
@Composable
private fun points(count: Int) = (1..count).map { Point(Random.nextFloat(), "Point$it") }

@Composable
fun PieChartComposable() {
PieChart(pies = Pies(listOf(Slice(35f, Red), Slice(45f, JetGreen), Slice(15f, Yellow), Slice(5f, Cyan))),
modifier = Modifier.height(340.dp),
animation = fadeInAnimation(4000),
sliceDrawer = FilledSliceDrawer(thickness = 60f)
)
}

@Composable
private fun JetDivider() {
Divider(modifier = Modifier.padding(horizontal = 5.dp, vertical = 50.dp), thickness = 1.dp, color = JetGreen)
Expand Down
Binary file added docs/pie1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/pie2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 0dfcec4

Please sign in to comment.