Skip to content

Commit 5ccc97a

Browse files
authored
Merge pull request #106 from AkYML/feature/bubbleChart
Bubble Chart implementation
2 parents 8f3d128 + 664c0a2 commit 5ccc97a

File tree

7 files changed

+168
-73
lines changed

7 files changed

+168
-73
lines changed

YChartsLib/src/main/java/co/yml/charts/axis/XAxis.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ fun getXAxisScale(
225225
private fun XAxisPreview() {
226226
val axisData = AxisData.Builder()
227227
.labelAndAxisLinePadding(10.dp)
228+
.startDrawPadding(12.dp)
228229
.axisPosition(Gravity.BOTTOM)
229230
.axisLabelFontSize(14.sp)
230231
.labelData { index -> index.toString() }

YChartsLib/src/main/java/co/yml/charts/common/utils/DataUtils.kt

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package co.yml.charts.common.utils
22

33
import androidx.compose.foundation.layout.Arrangement
44
import androidx.compose.ui.graphics.Color
5+
import androidx.compose.ui.graphics.TileMode
56
import androidx.compose.ui.text.TextStyle
67
import co.yml.charts.axis.DataCategoryOptions
78
import co.yml.charts.common.model.LegendLabel
@@ -12,6 +13,7 @@ import co.yml.charts.ui.barchart.models.BarChartType
1213
import co.yml.charts.ui.barchart.models.BarData
1314
import co.yml.charts.ui.barchart.models.GroupBar
1415
import co.yml.charts.ui.bubblechart.model.Bubble
16+
import co.yml.charts.ui.bubblechart.model.BubbleGradientType
1517
import co.yml.charts.ui.bubblechart.model.BubbleStyle
1618
import co.yml.charts.ui.piechart.models.PieChartData
1719
import kotlin.math.sin
@@ -48,7 +50,7 @@ object DataUtils {
4850
for (index in 0 until listSize) {
4951
list.add(
5052
Point(
51-
index.toFloat(),
53+
index.toFloat(),
5254
(start until maxRange).random().toFloat()
5355
)
5456
)
@@ -69,14 +71,68 @@ object DataUtils {
6971
): List<Bubble> {
7072
val list = arrayListOf<Bubble>()
7173
points.forEachIndexed { index, point ->
72-
val bubbleColor = if (index % 2 == 0) Color.Red else Color.Blue
73-
list.add(
74-
Bubble(
75-
center = point,
76-
density = (minDensity.toInt() until maxDensity.toInt()).random().toFloat(),
77-
bubbleStyle = BubbleStyle(gradientColors = listOf(bubbleColor, Color.White), useGradience = true)
78-
)
79-
)
74+
val bubbleColor1 = Color(Random.nextInt(256), Random.nextInt(256), Random.nextInt(256), Random.nextInt(256))
75+
val bubbleColor2 = Color(Random.nextInt(256), Random.nextInt(256), Random.nextInt(256), Random.nextInt(256))
76+
val bubbleColor3 = Color(Random.nextInt(256), Random.nextInt(256), Random.nextInt(256), Random.nextInt(256))
77+
val bubbleColor4 = Color(Random.nextInt(256), Random.nextInt(256), Random.nextInt(256), Random.nextInt(256))
78+
79+
when(Random.nextInt(0,5)){
80+
0->{
81+
list.add(
82+
Bubble(
83+
center = point,
84+
density = (minDensity.toInt() until maxDensity.toInt()).random().toFloat(),
85+
bubbleStyle = BubbleStyle(gradientColors = listOf(bubbleColor1, bubbleColor2,bubbleColor3,bubbleColor4), useGradience = true, gradientType = BubbleGradientType.RadialGradient)
86+
)
87+
)
88+
}
89+
1->{
90+
list.add(
91+
Bubble(
92+
center = point,
93+
density = (minDensity.toInt() until maxDensity.toInt()).random().toFloat(),
94+
bubbleStyle = BubbleStyle(gradientColors = listOf(bubbleColor1, bubbleColor2), useGradience = true, gradientType = BubbleGradientType.LinearGradient)
95+
)
96+
)
97+
}
98+
2->{
99+
list.add(
100+
Bubble(
101+
center = point,
102+
density = (minDensity.toInt() until maxDensity.toInt()).random().toFloat(),
103+
bubbleStyle = BubbleStyle(gradientColors = listOf(bubbleColor1, bubbleColor2), useGradience = true, gradientType = BubbleGradientType.VerticalGradient)
104+
)
105+
)
106+
}
107+
3->{
108+
list.add(
109+
Bubble(
110+
center = point,
111+
density = (minDensity.toInt() until maxDensity.toInt()).random().toFloat(),
112+
bubbleStyle = BubbleStyle(gradientColors = listOf(bubbleColor1, bubbleColor2), useGradience = true, gradientType = BubbleGradientType.HorizontalGradient)
113+
)
114+
)
115+
}
116+
4->{
117+
list.add(
118+
Bubble(
119+
center = point,
120+
density = (minDensity.toInt() until maxDensity.toInt()).random().toFloat(),
121+
bubbleStyle = BubbleStyle(gradientColors = listOf(bubbleColor1, bubbleColor2,bubbleColor3,bubbleColor4), useGradience = true, gradientType = BubbleGradientType.HorizontalGradient,tileMode = TileMode.Repeated)
122+
)
123+
)
124+
}
125+
5->{
126+
list.add(
127+
Bubble(
128+
center = point,
129+
density = (minDensity.toInt() until maxDensity.toInt()).random().toFloat(),
130+
bubbleStyle = BubbleStyle(gradientColors = listOf(bubbleColor1, bubbleColor2,bubbleColor3,bubbleColor4), useGradience = true, gradientType = BubbleGradientType.HorizontalGradient,tileMode = TileMode.Mirror)
131+
)
132+
)
133+
}
134+
}
135+
80136

81137
}
82138
return list
@@ -95,15 +151,14 @@ object DataUtils {
95151
): List<Bubble> {
96152
val list = arrayListOf<Bubble>()
97153
points.forEachIndexed { index, point ->
98-
val bubbleColor = if (index % 2 == 0) Color.Red else Color.Blue
154+
val bubbleColor =Color(red = Random.nextInt(256),green= Random.nextInt(256),blue= Random.nextInt(256), alpha =Random.nextInt(from = 150, until = 256) )
99155
list.add(
100156
Bubble(
101157
center = point,
102158
density = (minDensity.toInt() until maxDensity.toInt()).random().toFloat(),
103159
bubbleStyle = BubbleStyle(solidColor = bubbleColor, useGradience = false)
104160
)
105161
)
106-
107162
}
108163
return list
109164
}
@@ -140,15 +195,15 @@ object DataUtils {
140195
val point = when (barChartType) {
141196
BarChartType.VERTICAL -> {
142197
Point(
143-
index.toFloat(),
198+
index.toFloat(),
144199
"%.2f".format(Random.nextDouble(1.0, maxRange.toDouble())).toFloat()
145200
)
146201
}
147202

148203
BarChartType.HORIZONTAL -> {
149204
Point(
150205
"%.2f".format(Random.nextDouble(1.0, maxRange.toDouble())).toFloat(),
151-
index.toFloat()
206+
"%.2f".format(Random.nextDouble(1.0, maxRange.toDouble())).toFloat()
152207
)
153208
}
154209
}

YChartsLib/src/main/java/co/yml/charts/ui/bubblechart/BubbleChart.kt

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Column
88
import androidx.compose.foundation.layout.fillMaxHeight
99
import androidx.compose.foundation.layout.fillMaxSize
1010
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.padding
1112
import androidx.compose.foundation.layout.wrapContentHeight
1213
import androidx.compose.foundation.lazy.LazyColumn
1314
import androidx.compose.material.ExperimentalMaterialApi
@@ -34,6 +35,7 @@ import androidx.compose.ui.platform.LocalDensity
3435
import androidx.compose.ui.semantics.contentDescription
3536
import androidx.compose.ui.semantics.semantics
3637
import androidx.compose.ui.unit.Dp
38+
import androidx.compose.ui.unit.dp
3739
import co.yml.charts.axis.XAxis
3840
import co.yml.charts.axis.YAxis
3941
import co.yml.charts.axis.getXAxisScale
@@ -60,11 +62,10 @@ import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp
6062
import kotlinx.coroutines.launch
6163

6264
/**
65+
* Bubble chart
6366
*
64-
* [BubbleChart] compose method used for drawing a Bubble Chart.
65-
* @param modifier :All modifier related property.
66-
* Data class [BubbleChartData] to save all params needed to draw the bubble chart.
67-
* @param bubbleChartData : Add data related to bubble chart.
67+
* @param modifier
68+
* @param bubbleChartData
6869
*/
6970
@OptIn(ExperimentalMaterialApi::class)
7071
@Composable
@@ -130,13 +131,6 @@ fun BubbleChart(modifier: Modifier, bubbleChartData: BubbleChartData) {
130131
containerBackgroundColor = backgroundColor,
131132
isPinchZoomEnabled = isZoomAllowed,
132133
drawXAndYAxis = { scrollOffset, xZoom ->
133-
YAxis(
134-
modifier = Modifier
135-
.fillMaxHeight()
136-
.onGloballyPositioned {
137-
columnWidth = it.size.width.toFloat()
138-
}, yAxisData = yAxisData
139-
)
140134
XAxis(xAxisData = xAxisData,
141135
modifier = Modifier
142136
.fillMaxWidth()
@@ -155,6 +149,14 @@ fun BubbleChart(modifier: Modifier, bubbleChartData: BubbleChartData) {
155149
zoomScale = xZoom,
156150
chartData = bubblePoints,
157151
axisStart = columnWidth)
152+
YAxis(
153+
modifier = Modifier
154+
.fillMaxHeight()
155+
.onGloballyPositioned {
156+
columnWidth = it.size.width.toFloat()
157+
}, yAxisData = yAxisData
158+
)
159+
158160
},
159161
onDraw = { scrollOffset, xZoom ->
160162
val yBottom = size.height - rowHeight
@@ -182,9 +184,9 @@ fun BubbleChart(modifier: Modifier, bubbleChartData: BubbleChartData) {
182184
it
183185
)
184186
}
185-
pointsData.forEachIndexed {index,offset->
187+
pointsData.forEachIndexed { index, offset ->
186188

187-
bubbles[index].draw(this,offset)
189+
bubbles[index].draw(this, offset, bubbleChartData.maximumBubbleRadius)
188190

189191
pointsData.forEachIndexed { index, point ->
190192
if (isTapped && point.isTapped(tapOffset.x, xOffset)) {
@@ -272,15 +274,16 @@ fun BubbleChart(modifier: Modifier, bubbleChartData: BubbleChartData) {
272274
}
273275

274276
/**
277+
* Get max scroll distance
275278
*
276-
* returns the max scrollable distance based on the points to be drawn along with padding etc.
277-
* @param columnWidth : Width of the Y-Axis.
278-
* @param xMax : Max X-Axis value.
279-
* @param xMin: Min X-Axis value.
280-
* @param xOffset: Total distance between two X-Axis points.
281-
* @param paddingRight : Padding at the end of the canvas.
282-
* @param canvasWidth : Total available canvas width.
283-
* @param containerPaddingEnd : Container inside padding end after the last point of the graph.
279+
* @param columnWidth
280+
* @param xMax
281+
* @param xMin
282+
* @param xOffset
283+
* @param paddingRight
284+
* @param canvasWidth
285+
* @param containerPaddingEnd
286+
* @return
284287
*/
285288
fun getMaxScrollDistance(
286289
columnWidth: Float,
@@ -317,9 +320,10 @@ private fun DrawScope.drawUnderScrollMask(columnWidth: Float, paddingRight: Dp,
317320

318321

319322
/**
323+
* Get cubic points
320324
*
321-
* getCubicPoints method provides left and right average value for a given point to get a smooth curve.
322-
* @param pointsData : List of the points on the Line graph.
325+
* @param pointsData
326+
* @return
323327
*/
324328
fun getCubicPoints(pointsData: List<Offset>): Pair<MutableList<Offset>, MutableList<Offset>> {
325329
val cubicPoints1 = mutableListOf<Offset>()
@@ -341,10 +345,11 @@ fun getCubicPoints(pointsData: List<Offset>): Pair<MutableList<Offset>, MutableL
341345
}
342346

343347
/**
344-
* Used to draw the highlighted text
345-
* @param identifiedPoint : Selected points
346-
* @param selectedOffset: Offset selected
347-
* @param selectionHighlightPopUp : Data class with all styling related info [SelectionHighlightPopUp]
348+
* Draw highlight text
349+
*
350+
* @param identifiedPoint
351+
* @param selectedOffset
352+
* @param selectionHighlightPopUp
348353
*/
349354
fun DrawScope.drawHighlightText(
350355
identifiedPoint: Point,
@@ -357,14 +362,13 @@ fun DrawScope.drawHighlightText(
357362
}
358363

359364
/**
365+
* Draw high light on selected point
360366
*
361-
* DrawScope.drawHighLightOnSelectedPoint extension method used for drawing and highlight the selected
362-
* point when dragging.
363-
* @param dragLocks : List of points to be drawn on the canvas and that can be selected.
364-
* @param columnWidth : Width of the Axis in the left or right.
365-
* @param paddingRight : Padding given to the right side of the canvas.
366-
* @param yBottom : Start position from below of the canvas.
367-
* @param selectionHighlightPoint : Data class to define all the styles to be drawn in [SelectionHighlightPoint]
367+
* @param dragLocks
368+
* @param columnWidth
369+
* @param paddingRight
370+
* @param yBottom
371+
* @param selectionHighlightPoint
368372
*/
369373
fun DrawScope.drawHighLightOnSelectedPoint(
370374
dragLocks: MutableMap<Int, Pair<Point, Offset>>,

YChartsLib/src/main/java/co/yml/charts/ui/bubblechart/model/BubbleChartData.kt

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package co.yml.charts.ui.bubblechart.model
33
import androidx.compose.ui.geometry.Offset
44
import androidx.compose.ui.graphics.Brush
55
import androidx.compose.ui.graphics.Color
6-
import androidx.compose.ui.graphics.TileMode
76
import androidx.compose.ui.graphics.drawscope.DrawScope
87
import androidx.compose.ui.unit.Dp
98
import androidx.compose.ui.unit.dp
@@ -20,6 +19,7 @@ import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp
2019
* Bubble chart data
2120
*
2221
* @property bubbles
22+
* @property maximumBubbleRadius
2323
* @property xAxisData
2424
* @property yAxisData
2525
* @property isZoomAllowed
@@ -34,6 +34,7 @@ import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp
3434
*/
3535
data class BubbleChartData(
3636
val bubbles: List<Bubble>,
37+
val maximumBubbleRadius:Float = 100f,
3738
val xAxisData: AxisData = AxisData.Builder().build(),
3839
val yAxisData: AxisData = AxisData.Builder().build(),
3940
val isZoomAllowed: Boolean = true,
@@ -65,12 +66,13 @@ data class Bubble(
6566
val intersectionPoint: IntersectionPoint? = null,
6667
val selectionHighlightPoint: SelectionHighlightPoint? = null,
6768
val selectionHighlightPopUp: SelectionHighlightPopUp? = null,
68-
val draw: DrawScope.(Offset) -> Unit = { center ->
69+
val draw: DrawScope.(Offset,Float) -> Unit = { center,maximumRadius ->
70+
val drawingRadius:Float = (density / maximumRadius) * 100
6971
if (bubbleStyle.useGradience) {
7072
drawCircle(
7173
brush = getBrush(bubbleStyle, center, density),
7274
center = center,
73-
radius = density,
75+
radius = drawingRadius,
7476
alpha = bubbleStyle.alpha,
7577
style = bubbleStyle.style,
7678
colorFilter = bubbleStyle.colorFilter,
@@ -79,7 +81,7 @@ data class Bubble(
7981
} else {
8082
drawCircle(
8183
bubbleStyle.solidColor,
82-
density,
84+
drawingRadius,
8385
center,
8486
bubbleStyle.alpha,
8587
bubbleStyle.style,
@@ -91,10 +93,43 @@ data class Bubble(
9193
)
9294

9395
private fun getBrush(bubbleStyle: BubbleStyle, center: Offset, density: Float): Brush {
94-
return Brush.radialGradient(
95-
colors = bubbleStyle.gradientColors,
96-
center = center,
97-
radius = density,
98-
tileMode = TileMode.Decal
99-
)
96+
when (bubbleStyle.gradientType) {
97+
BubbleGradientType.RadialGradient -> {
98+
return Brush.radialGradient(
99+
colors = bubbleStyle.gradientColors,
100+
center = center,
101+
radius = density,
102+
tileMode = bubbleStyle.tileMode
103+
)
104+
}
105+
106+
BubbleGradientType.LinearGradient -> {
107+
return Brush.linearGradient(
108+
colors = bubbleStyle.gradientColors,
109+
tileMode = bubbleStyle.tileMode,
110+
start = center,
111+
end = center
112+
)
113+
}
114+
115+
BubbleGradientType.VerticalGradient -> {
116+
return Brush.verticalGradient(
117+
colors = bubbleStyle.gradientColors,
118+
tileMode = bubbleStyle.tileMode,
119+
startY = center.y - density / 2,
120+
endY = center.y + density / 2,
121+
)
122+
}
123+
124+
BubbleGradientType.HorizontalGradient -> {
125+
return Brush.horizontalGradient(
126+
colors = bubbleStyle.gradientColors,
127+
tileMode = bubbleStyle.tileMode,
128+
startX = center.x - density / 2,
129+
endX = center.x + density / 2,
130+
)
131+
}
132+
}
133+
134+
100135
}

0 commit comments

Comments
 (0)