Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced Cubic Bezier algorithm for prettier line charts #150

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
# YCharts
This is a fork the YCharts library using Advanced Cubic Bezier algorithm for prettier line charts.
The algorithm is explained in detail at [https://medium.com/mobile-app-development-publication/making-graph-plotting-function-in-jetpack-compose-95c80ee6fc7f](https://medium.com/mobile-app-development-publication/making-graph-plotting-function-in-jetpack-compose-95c80ee6fc7f)
<div>
<figure>
<img width=100 src="https://github.com/codeandtheory/YCharts/assets/6216877/f9bf3760-ca16-4644-8abc-b1326d28b8d3"/>
</figure>
</div>
Advanced Cubic Bezier
<div>
<figure>
<img width=100 src="https://github.com/codeandtheory/YCharts/assets/6216877/12a086eb-3fe0-43e2-b9ed-2e1dafbab8ad"/>
</figure>
</div>
Basic Cubic Bezier

YCharts is a Jetpack-compose based charts library which enables developers to easily integrate various types of charts/graphs into their existing ui to visually represent statistical data. YCharts supports both cartesian(XY-charts) and polar charts(Radial charts), which include:

Expand Down
2 changes: 1 addition & 1 deletion YChartsLib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

android {
compileSdk = 33
compileSdk = 35
namespace = "co.yml.charts.components"
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import co.yml.charts.ui.linechart.model.LineType
import co.yml.charts.ui.linechart.model.SelectionHighlightPoint
import co.yml.charts.ui.linechart.model.SelectionHighlightPopUp
import kotlinx.coroutines.launch
import kotlin.math.sqrt

/**
*
Expand Down Expand Up @@ -450,25 +451,57 @@ fun DrawScope.drawShadowUnderLineAndIntersectionPoint(
}
}

private fun getInBetweenSlope(m1: Float, m2: Float) : Float {
return if (m1 == m2) m1 else (m1 + m2) / ( 1 - m1 * m2 + sqrt(((m1*m1) + 1) * ((m2*m2) + 1)) )
}

private fun getSlope(p1: Offset, p2: Offset): Float {
return (p2.y-p1.y) / (p2.x-p1.x)
}
/**
*
* getCubicPoints method provides left and right average value for a given point to get a smooth curve.
* Using the Advanced Cubic Bezier method as given in https://medium.com/mobile-app-development-publication/making-graph-plotting-function-in-jetpack-compose-95c80ee6fc7f
* @param pointsData : List of the points on the Line graph.
*/
fun getCubicPoints(pointsData: List<Offset>): Pair<MutableList<Offset>, MutableList<Offset>> {
val cubicPoints1 = mutableListOf<Offset>()
val cubicPoints2 = mutableListOf<Offset>()

val slopes = FloatArray(pointsData.size)

for (i in 1 until pointsData.size) {
val currSlope = if (i == 1) getSlope(pointsData[1], pointsData[0]) else slopes [i]

val nextSlope: Float
if (i < pointsData.size-1) {
nextSlope = getSlope(pointsData[i + 1], pointsData[i])
slopes[i+1] = nextSlope
}
else {
nextSlope = currSlope
}

val prevSlope = if (i >1 ) slopes[i-1] else currSlope

val cp1x = ((pointsData[i-1].x * 2f) + pointsData[i].x)/3f
val cp1slope = getInBetweenSlope(prevSlope, currSlope)
val cp1c = pointsData[i-1].y - (cp1slope * pointsData[i-1].x)
val cp1y = (cp1slope * cp1x) + cp1c

val cp2x = (pointsData[i-1].x + (pointsData[i].x * 2f))/3f
val cp2slope = getInBetweenSlope(currSlope, nextSlope)
val cp2c = pointsData[i].y - (cp2slope * pointsData[i].x)
val cp2y = (cp2slope * cp2x ) + cp2c

cubicPoints1.add(
Offset(
(pointsData[i].x + pointsData[i - 1].x) / 2, pointsData[i - 1].y
cp1x, cp1y
)
)
cubicPoints2.add(
Offset(
(pointsData[i].x + pointsData[i - 1].x) / 2, pointsData[i].y
cp2x, cp2y
)
)
}
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ buildscript {
dependencies {
classpath(versionCatalogLibs.android.gradle.plugin)
classpath(versionCatalogLibs.kotlin.gradle.plugin)
classpath("org.jetbrains.kotlin:kotlin-android-extensions:1.8.20")
classpath("org.jetbrains.kotlin:kotlin-android-extensions:1.9.23")
}

tasks {
Expand Down
42 changes: 21 additions & 21 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,47 @@
# Define the dependency versions
#Project
#kotlin
kotlinVersion = "1.8.20"
kotlinVersion = "1.9.21"
kotlinxSerializationJson = "1.5.0"
#gradle
androidGradlePlugin = "8.0.2"

#Modules
#androidx
androidxComposeBom = "2023.05.01"
androidxActivityCompose = "1.7.0"
androidxAppCompat = "1.6.1"
androidxComposeCompiler = "1.4.6"
androidxCore = "1.9.0"
androidxLifecycle = "2.6.1"
androidxNavigation = "2.5.3"
androidxEspresso = "3.5.1"
androidxTestRules = "1.5.0"
androidxTestRunner = "1.5.2"
androidxTestMonitor = "1.6.1"
androidxTestCore = "1.5.0"
androidxTestExt = "1.1.5"
androidxComposeBom = "2024.12.01"
androidxActivityCompose = "1.9.3"
androidxAppCompat = "1.7.0"
androidxComposeCompiler = "1.5.7"
androidxCore = "1.15.0"
androidxLifecycle = "2.8.7"
androidxNavigation = "2.8.5"
androidxEspresso = "3.6.1"
androidxTestRules = "1.6.1"
androidxTestRunner = "1.6.2"
androidxTestMonitor = "1.7.2"
androidxTestCore = "1.6.1"
androidxTestExt = "1.2.1"
androidxCrypto = "1.1.0-alpha04"
androidxDatastore = "1.0.0"

#compose
compose_ui = "1.4.3"
compose_ui_testing = "1.4.3"
compose_ui = "1.7.6"
compose_ui_testing = "1.7.6"
compose_constraint_layout = "1.1.0-alpha05"
#ktx
core_ktx = "1.9.0"
core_ktx = "1.15.0"
#coroutine
coroutine = "1.7.1"
coroutine = "1.8.1"
turbine = "1.0.0"
#android test
junit4 = "4.13.2"
android_junit = "1.1.5"
android_junit = "1.2.1"
espresso = "3.5.0"
jupiter_junit = "5.9.2"
#Mock
anotation = "1.6.0"
anotation = "1.9.1"
mockk_android = "1.13.4"
test_runner = "1.5.2"
test_runner = "1.6.2"
#sonar
sonar = "4.0.0.2929"
#dokka
Expand Down