Skip to content

Commit

Permalink
Improve bar chart code (#121)
Browse files Browse the repository at this point in the history
  • Loading branch information
dautovicharis authored Jun 17, 2024
1 parent 9765d39 commit 2d6228d
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.dautovicharis.charts.internal.barchart

import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.runtime.Composable
Expand All @@ -11,6 +12,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.util.lerp
import io.github.dautovicharis.charts.internal.AnimationSpec
Expand Down Expand Up @@ -63,30 +66,52 @@ internal fun BarChart(
onValueChanged(NO_SELECTION)
}
)
}) {
val baselineY = size.height * (maxValue / (maxValue - minValue))
val dataSize = chartData.points.size
}, onDraw = {
drawBars(
style = style,
size = size,
chartData = chartData,
progress = progress,
selectedIndex = selectedIndex,
barColor = barColor,
maxValue = maxValue,
minValue = minValue
)
})
}

private fun DrawScope.drawBars(
style: BarChartStyle,
size: Size,
chartData: ChartData,
progress: List<Animatable<Float, AnimationVector1D>>,
selectedIndex: Int,
barColor: Color,
maxValue: Double,
minValue: Double
) {
val baselineY = size.height * (maxValue / (maxValue - minValue))
val dataSize = chartData.points.size

chartData.points.forEachIndexed { index, value ->
val spacing = style.space.toPx()
val barWidth = (size.width - spacing * (dataSize - 1)) / dataSize
chartData.points.forEachIndexed { index, value ->
val spacing = style.space.toPx()
val barWidth = (size.width - spacing * (dataSize - 1)) / dataSize

val finalBarHeight = size.height * (abs(value) / (maxValue - minValue))
val barHeight = lerp(0f, finalBarHeight.toFloat(), progress[index].value)
val finalBarHeight = size.height * (abs(value) / (maxValue - minValue))
val barHeight = lerp(0f, finalBarHeight.toFloat(), progress[index].value)

val top = if (value >= 0) baselineY - barHeight else baselineY
val left = (barWidth + spacing) * index
val top = if (value >= 0) baselineY - barHeight else baselineY
val left = (barWidth + spacing) * index

val selectedBarScale = if (index == selectedIndex) MAX_SCALE else DEFAULT_SCALE
val selectedBarScale = if (index == selectedIndex) MAX_SCALE else DEFAULT_SCALE

drawRect(
color = barColor,
topLeft = Offset(x = left, y = top.toFloat()),
size = Size(
width = barWidth * selectedBarScale,
height = barHeight * selectedBarScale
)
drawRect(
color = barColor,
topLeft = Offset(x = left, y = top.toFloat()),
size = Size(
width = barWidth * selectedBarScale,
height = barHeight * selectedBarScale
)
}
)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.dautovicharis.charts.internal.barstackedchart

import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.runtime.Composable
Expand All @@ -12,6 +13,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.util.lerp
import io.github.dautovicharis.charts.internal.AnimationSpec
Expand All @@ -33,6 +35,7 @@ internal fun StackedBarChart(
val progress = remember {
data.items.map { Animatable(0f) }
}
var selectedIndex by remember { mutableIntStateOf(-1) }

progress.forEachIndexed { index, _ ->
LaunchedEffect(index) {
Expand All @@ -43,8 +46,6 @@ internal fun StackedBarChart(
}
}

var selectedIndex by remember { mutableIntStateOf(-1) }

Canvas(
modifier = style.modifier.pointerInput(Unit) {
detectDragGestures(
Expand All @@ -62,32 +63,50 @@ internal fun StackedBarChart(
onValueChanged(NO_SELECTION)
}
)
}, onDraw = {
drawBars(
style = style,
size = size,
data = data,
progress = progress,
selectedIndex = selectedIndex,
colors = colors
)
}
) {
val totalMaxValue = data.items.maxOf { it.item.points.sum() }
val spacing = style.space.toPx()
val barWidth = (size.width - spacing * (data.items.size - 1)) / data.items.size
)
}

data.items.forEachIndexed { index, item ->
var topOffset = size.height
val selectedBarScale = if (index == selectedIndex) MAX_SCALE else DEFAULT_SCALE
item.item.points.forEachIndexed { dataIndex, value ->
val height = lerp(
0f,
(value.toFloat() / totalMaxValue.toFloat()) * size.height,
progress[index].value
)
topOffset -= height
private fun DrawScope.drawBars(
style: StackedBarChartStyle,
size: Size,
data: MultiChartData,
progress: List<Animatable<Float, AnimationVector1D>>,
selectedIndex: Int,
colors: List<Color>
) {
val totalMaxValue = data.items.maxOf { it.item.points.sum() }
val spacing = style.space.toPx()
val barWidth = (size.width - spacing * (data.items.size - 1)) / data.items.size

drawRect(
color = colors[dataIndex],
topLeft = Offset(x = index * (barWidth + spacing), y = topOffset),
size = Size(
width = barWidth * selectedBarScale,
height = height * selectedBarScale
)
data.items.forEachIndexed { index, item ->
var topOffset = size.height
val selectedBarScale = if (index == selectedIndex) MAX_SCALE else DEFAULT_SCALE
item.item.points.forEachIndexed { dataIndex, value ->
val height = lerp(
0f,
(value.toFloat() / totalMaxValue.toFloat()) * size.height,
progress[index].value
)
topOffset -= height

drawRect(
color = colors[dataIndex],
topLeft = Offset(x = index * (barWidth + spacing), y = topOffset),
size = Size(
width = barWidth * selectedBarScale,
height = height * selectedBarScale
)
}
)
}
}
}

0 comments on commit 2d6228d

Please sign in to comment.