Full Code of bytebeats/compose-charts for AI

master 06a4fc707b1c cached
83 files
151.8 KB
40.4k tokens
1 requests
Download .txt
Repository: bytebeats/compose-charts
Branch: master
Commit: 06a4fc707b1c
Files: 83
Total size: 151.8 KB

Directory structure:
gitextract_1pinupfr/

├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── bytebeats/
│           │           └── views/
│           │               └── charts/
│           │                   └── app/
│           │                       ├── MainActivity.kt
│           │                       └── ui/
│           │                           ├── ComposeCharts.kt
│           │                           ├── Screen.kt
│           │                           ├── ScreenRouter.kt
│           │                           ├── screen/
│           │                           │   ├── HomeScreen.kt
│           │                           │   ├── bar/
│           │                           │   │   ├── BarChartDataModel.kt
│           │                           │   │   └── BarChartScreen.kt
│           │                           │   ├── line/
│           │                           │   │   ├── LineChartDataModel.kt
│           │                           │   │   ├── LineChartScreen.kt
│           │                           │   │   └── PointDrawerType.kt
│           │                           │   └── pie/
│           │                           │       ├── PieChartDataModel.kt
│           │                           │       └── PieChartScreen.kt
│           │                           └── theme/
│           │                               ├── Color.kt
│           │                               ├── Margin.kt
│           │                               ├── Shape.kt
│           │                               ├── Theme.kt
│           │                               └── Type.kt
│           └── res/
│               ├── drawable/
│               │   └── ic_launcher_background.xml
│               ├── drawable-v24/
│               │   └── ic_launcher_foreground.xml
│               ├── mipmap-anydpi-v26/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── themes.xml
├── build.gradle.kts
├── charts/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── consumer-rules.pro
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           └── java/
│               └── me/
│                   └── bytebeats/
│                       └── views/
│                           └── charts/
│                               ├── Animations.kt
│                               ├── AxisLine.kt
│                               ├── Colors.kt
│                               ├── TypeAlias.kt
│                               ├── bar/
│                               │   ├── BarChart.kt
│                               │   ├── BarChartData.kt
│                               │   ├── BarCharts.kt
│                               │   └── render/
│                               │       ├── bar/
│                               │       │   ├── IBarDrawer.kt
│                               │       │   └── SimpleBarDrawer.kt
│                               │       ├── label/
│                               │       │   ├── ILabelDrawer.kt
│                               │       │   └── SimpleLabelDrawer.kt
│                               │       ├── xaxis/
│                               │       │   ├── IXAxisDrawer.kt
│                               │       │   └── SimpleXAxisDrawer.kt
│                               │       └── yaxis/
│                               │           ├── IYAxisDrawer.kt
│                               │           └── SimpleYAxisDrawer.kt
│                               ├── line/
│                               │   ├── LineChart.kt
│                               │   ├── LineChartData.kt
│                               │   ├── LineCharts.kt
│                               │   └── render/
│                               │       ├── line/
│                               │       │   ├── EmptyLineShader.kt
│                               │       │   ├── GradientLineShader.kt
│                               │       │   ├── ILineDrawer.kt
│                               │       │   ├── ILineShader.kt
│                               │       │   ├── SolidLineDrawer.kt
│                               │       │   └── SolidLineShader.kt
│                               │       ├── point/
│                               │       │   ├── EmptyPointDrawer.kt
│                               │       │   ├── FilledCircularPointDrawer.kt
│                               │       │   ├── HollowCircularPointDrawer.kt
│                               │       │   └── IPointDrawer.kt
│                               │       ├── xaxis/
│                               │       │   ├── IXAxisDrawer.kt
│                               │       │   └── SimpleXAxisDrawer.kt
│                               │       └── yaxis/
│                               │           ├── IYAxisDrawer.kt
│                               │           └── SimpleYAxisDrawer.kt
│                               ├── pie/
│                               │   ├── PieChart.kt
│                               │   ├── PieChartData.kt
│                               │   ├── PieCharts.kt
│                               │   └── render/
│                               │       ├── ISliceDrawer.kt
│                               │       └── SimpleSliceDrawer.kt
│                               └── util/
│                                   └── Floats.kt
├── config/
│   └── detekt/
│       └── detekt.yml
├── gradle/
│   ├── libs.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/.idea/
out/
/captures
.externalNativeBuild
.cxx
local.properties


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Chen Pan

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# compose-charts

[![GitHub latest commit](https://badgen.net/github/last-commit/bytebeats/compose-charts)](https://github.com/bytebeats/compose-charts/commit/)
[![GitHub contributors](https://img.shields.io/github/contributors/bytebeats/compose-charts.svg)](https://github.com/bytebeats/compose-charts/graphs/contributors/)
[![GitHub issues](https://img.shields.io/github/issues/bytebeats/compose-charts.svg)](https://github.com/bytebeats/compose-charts/issues/)
[![Open Source? Yes!](https://badgen.net/badge/Open%20Source%20%3F/Yes%21/blue?icon=github)](https://github.com/bytebeats/compose-charts/)
[![GitHub forks](https://img.shields.io/github/forks/bytebeats/compose-charts.svg?style=social&label=Fork&maxAge=2592000)](https://github.com/bytebeats/compose-charts/network/)
[![GitHub stars](https://img.shields.io/github/stars/bytebeats/compose-charts.svg?style=social&label=Star&maxAge=2592000)](https://github.com/bytebeats/compose-charts/stargazers/)
[![GitHub watchers](https://img.shields.io/github/watchers/bytebeats/compose-charts.svg?style=social&label=Watch&maxAge=2592000)](https://github.com/bytebeats/compose-charts/watchers/)

Simple Jetpack Compose Charts for multi-platform. Including Android, Web, Desktop.

Compose Multiplatform for Desktop: [compose-charts-desktop](https://github.com/bytebeats/compose-charts-desktop).

**LATEST_VERSION**: 0.2.1

**COMPOSE_VERSION**: 1.6.7

**KOTLIN_VERSION**: 1.9.24

## Graph Effects

<img src="/arts/pie_chart.gif" width="220" height="480"/><img src="/arts/line_chart.gif" width="220" height="480"/><img src="/arts/bar_chart.gif" width="220" height="480"/>

## How to use?

1. add maven and dependency:

1.1. add specific maven url in your root `build.gradle.kts`

```
    repositories {
       ...
       maven {
            url = uri("https://repo1.maven.org/maven2/")
       }
    }
```

1.2. add dependency in your module `build.gradle.kts`

```
dependencies {
    implementation("androidx.compose.ui:ui:$compose_version")

//    implementation(project(":charts"))
    implementation("io.github.bytebeats:compose-charts:${LATEST_VERSION}")
}
```

2. show Pie Chart in Jetpack Compose:

```
@Composable
fun PieChartView() {
    PieChart(
        pieChartData = PieChartData(
            slices = listOf(
                PieChartData.Slice(
                    randomLength(),
                    randomColor()
                ),
                PieChartData.Slice(randomLength(), randomColor()),
                PieChartData.Slice(randomLength(), randomColor())
            )
        ),
        // Optional properties.
        modifier = Modifier.fillMaxSize(),
        animation = simpleChartAnimation(),
        sliceDrawer = SimpleSliceDrawer()
    )
}
```

3. show Line Chart in Jetpack Compose:

```
@Composable
fun LineChartView() {
    LineChart(
        lineChartData = LineChartData(
            points = listOf(
                Point(randomYValue(), "Line 1"),
                Point(randomYValue(), "Line 2"),
                Point(randomYValue(), "Line 3"),
                Point(randomYValue(), "Line 4"),
                Point(randomYValue(), "Line 5"),
                Point(randomYValue(), "Line 6"),
                Point(randomYValue(), "Line 7")
            )
        ),
        // Optional properties.
        modifier = Modifier.fillMaxSize(),
        animation = simpleChartAnimation(),
        pointDrawer = FilledCircularPointDrawer(),
        lineDrawer = SolidLineDrawer(),
        xAxisDrawer = SimpleXAxisDrawer(),
        yAxisDrawer = SimpleYAxisDrawer(),
        horizontalOffset = 5f
    )
}
```

4. show Bar Chart in Jetpack Compose:

```
@Composable
fun BarChartView() {
    BarChart(
        barChartData = BarChartData(
            bars = listOf(
                BarChartData.Bar(
                    label = "Bar 1",
                    value = randomValue(),
                    color = randomColor()
                ),
                BarChartData.Bar(
                    label = "Bar 2",
                    value = randomValue(),
                    color = randomColor()
                ),
                BarChartData.Bar(
                    label = "Bar 3",
                    value = randomValue(),
                    color = randomColor()
                ),
                BarChartData.Bar(
                    label = "Bar 4",
                    value = randomValue(),
                    color = randomColor()
                ),
            )
        ),
        // Optional properties.
        modifier = Modifier.fillMaxSize(),
        animation = simpleChartAnimation(),
        barDrawer = SimpleBarDrawer(),
        xAxisDrawer = SimpleXAxisDrawer(),
        yAxisDrawer = SimpleYAxisDrawer(),
        labelDrawer = SimpleLabelDrawer()
    ) 
}
```
## Stargazers over time

[![Stargazers over time](https://starchart.cc/bytebeats/compose-charts.svg)](https://starchart.cc/bytebeats/compose-charts)

## Github Stars Sparklines

[![Sparkline](https://stars.medv.io/bytebeats/compose-charts.svg)](https://stars.medv.io/bytebeats/compose-charts)

## Contributors

[![Contributors over time](https://contributor-graph-api.apiseven.com/contributors-svg?chart=contributorOverTime&repo=bytebeats/compose-charts)](https://www.apiseven.com/en/contributor-graph?chart=contributorOverTime&repo=bytebeats/compose-charts)

## MIT License

    Copyright (c) 2021 Chen Pan

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.


================================================
FILE: app/.gitignore
================================================
/build
.idea/
out/

================================================
FILE: app/build.gradle.kts
================================================
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    alias(libs.plugins.detekt.gradle.plugin)
}

android {
    namespace = "me.bytebeats.views.charts.app"
    compileSdk = 34

    defaultConfig {
        applicationId = "me.bytebeats.views.charts.app"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = libs.versions.ktCompilerExt.get()
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.activity.compose)

    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)

//    implementation(project(":charts"))
     implementation(libs.compose.charts)
}


================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Composecharts">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:theme="@style/Theme.Composecharts">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/MainActivity.kt
================================================
package me.bytebeats.views.charts.app

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import me.bytebeats.views.charts.app.ui.ComposeCharts


/**
 * Main activity
 */
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeCharts()
        }
    }
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/ComposeCharts.kt
================================================
package me.bytebeats.views.charts.app.ui

import androidx.compose.animation.Crossfade
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import me.bytebeats.views.charts.app.ui.screen.HomeScreen
import me.bytebeats.views.charts.app.ui.screen.bar.BarChartScreen
import me.bytebeats.views.charts.app.ui.screen.line.LineChartScreen
import me.bytebeats.views.charts.app.ui.screen.pie.PieChartScreen
import me.bytebeats.views.charts.app.ui.theme.ComposeChartsTheme

/**
 * Created by bytebeats on 2021/9/30 : 16:24
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

@Composable
fun ComposeCharts() {
    ComposeChartsTheme {
        ComposeChartsContent()
    }
}

@Composable
private fun ComposeChartsContent() {
    Crossfade(
        targetState = ScreenRouter.currentScreen,
        label = "Compose Charts"
    ) { screen ->
        Surface(
            color = MaterialTheme.colorScheme.background
        ) {
            when (screen) {
                Screen.Pie -> PieChartScreen()
                Screen.Line -> LineChartScreen()
                Screen.Bar -> BarChartScreen()
                else -> HomeScreen()
            }
        }
    }
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/Screen.kt
================================================
package me.bytebeats.views.charts.app.ui

/**
 * Created by bytebeats on 2021/9/30 : 11:34
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
enum class Screen {
    Home,
    Pie,
    Bar,
    Line;
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/ScreenRouter.kt
================================================
package me.bytebeats.views.charts.app.ui

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue

/**
 * Created by bytebeats on 2021/9/30 : 11:39
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
object ScreenRouter {
    var currentScreen by mutableStateOf(Screen.Home)

    /**
     * Navigate
     *
     * @param screen
     */
    fun navigate(screen: Screen) {
        currentScreen = screen
    }

    /**
     * Navigate home
     */
    fun navigateHome() {
        currentScreen = Screen.Home
    }
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/screen/HomeScreen.kt
================================================
package me.bytebeats.views.charts.app.ui.screen

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import me.bytebeats.views.charts.app.ui.Screen
import me.bytebeats.views.charts.app.ui.ScreenRouter
import me.bytebeats.views.charts.app.ui.theme.Margin

/**
 * Created by bytebeats on 2021/9/30 : 11:43
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text(text = "Compose Charts")
                }
            )
        }
    ) { paddingValues ->
        HomeScreenContent(Modifier.padding(paddingValues))
    }
}

@Composable
private fun HomeScreenContent(modifier: Modifier) {
    Column(
        modifier = modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        ChartScreenSelector(
            text = "Pie Chart",
            nextScreen = Screen.Pie
        )
        ChartScreenSelector(
            text = "Line Chart",
            nextScreen = Screen.Line
        )
        ChartScreenSelector(
            text = "Bar Chart",
            nextScreen = Screen.Bar
        )
    }
}

@Composable
private fun ChartScreenSelector(
    text: String,
    nextScreen: Screen
) {
    Row(
        modifier = Modifier.padding(
            horizontal = Margin.horizontal,
            vertical = Margin.vertical
        )
    ) {
        TextButton(
            onClick = { ScreenRouter.navigate(nextScreen) }
        ) {
            Text(text = text)
        }
    }
}

@Preview
@Composable
private fun HomeScreenPreview() = HomeScreen()


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/screen/bar/BarChartDataModel.kt
================================================
package me.bytebeats.views.charts.app.ui.screen.bar

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import me.bytebeats.views.charts.bar.BarChartData
import me.bytebeats.views.charts.bar.render.label.SimpleLabelDrawer
import kotlin.random.Random


/**
 * Created by bytebeats on 2021/9/30 : 19:39
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
class BarChartDataModel {
    private var colors = mutableListOf(
        Color(0XFFF44336),
        Color(0XFFE91E63),
        Color(0XFF9C27B0),
        Color(0XFF673AB7),
        Color(0XFF3F51B5),
        Color(0XFF03A9F4),
        Color(0XFF009688),
        Color(0XFFCDDC39),
        Color(0XFFFFC107),
        Color(0XFFFF5722),
        Color(0XFF795548),
        Color(0XFF9E9E9E),
        Color(0XFF607D8B)
    )

    var labelDrawer by mutableStateOf(SimpleLabelDrawer(drawLocation = SimpleLabelDrawer.DrawLocation.Inside))
        private set

    var barChartData by mutableStateOf(
        BarChartData(
            bars = listOf(
                BarChartData.Bar(
                    label = "Bar 1",
                    value = randomValue(),
                    color = randomColor()
                ),
                BarChartData.Bar(
                    label = "Bar 2",
                    value = randomValue(),
                    color = randomColor()
                ),
                BarChartData.Bar(
                    label = "Bar 3",
                    value = randomValue(),
                    color = randomColor()
                ),
                BarChartData.Bar(
                    label = "Bar 4",
                    value = randomValue(),
                    color = randomColor()
                ),
            )
        )
    )

    val bars: List<BarChartData.Bar>
        get() = barChartData.bars

    var labelLocation: SimpleLabelDrawer.DrawLocation = SimpleLabelDrawer.DrawLocation.Inside
        set(value) {
            val color = when (value) {
                SimpleLabelDrawer.DrawLocation.Inside -> Color.White
                SimpleLabelDrawer.DrawLocation.Outside, SimpleLabelDrawer.DrawLocation.XAxis -> Color.Black
            }
            labelDrawer = SimpleLabelDrawer(drawLocation = value, labelTextColor = color)
            field = value
        }

    internal fun addBar() {
        barChartData = barChartData.copy(bars = bars.toMutableList().apply {
            add(
                BarChartData.Bar(
                    label = "Bar ${bars.size + 1}",
                    value = randomValue(),
                    color = randomColor()
                )
            )
        }.toList())
    }

    internal fun removeBar() {
        barChartData = barChartData.copy(bars = bars.toMutableList().apply {
            val lastBar = bars.last()
            colors.add(lastBar.color)
            remove(lastBar)
        })
    }

    private fun randomValue(): Float = Random.Default.nextInt(25, 125).toFloat()
    private fun randomColor(): Color {
        val idx = Random.Default.nextInt(colors.size)
        return colors.removeAt(idx)
    }
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/screen/bar/BarChartScreen.kt
================================================
package me.bytebeats.views.charts.app.ui.screen.bar

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import me.bytebeats.views.charts.app.ui.ScreenRouter
import me.bytebeats.views.charts.app.ui.theme.Margin
import me.bytebeats.views.charts.bar.BarChart
import me.bytebeats.views.charts.bar.render.label.SimpleLabelDrawer.DrawLocation
import me.bytebeats.views.charts.bar.render.yaxis.SimpleYAxisDrawer

/**
 * Created by bytebeats on 2021/9/30 : 19:53
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BarChartScreen() {
    Scaffold(
        topBar = {
            TopAppBar(
                navigationIcon = {
                    IconButton(
                        onClick = { ScreenRouter.navigateHome() }
                    ) {
                        Icon(
                            imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                            contentDescription = "Go back home"
                        )
                    }
                },
                title = { Text(text = "Bar Chart") })
        }) { paddingValues ->
        BarChartContent(Modifier.padding(paddingValues))
    }
}

@Composable
private fun BarChartContent(modifier: Modifier = Modifier) {
    val barChartDataModel = BarChartDataModel()
    Column(
        modifier = modifier.padding(
            horizontal = Margin.horizontal,
            vertical = Margin.vertical
        )
    ) {
        BarChartRow(barChartDataModel = barChartDataModel)
        DrawLabelLocation(
            barChartDataModel = barChartDataModel,
            newLocation = { barChartDataModel.labelLocation = it }
        )
        AddOrRemoveBar(barChartDataModel = barChartDataModel)
    }
}

@Composable
private fun BarChartRow(barChartDataModel: BarChartDataModel) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .height(280.dp)
            .padding(vertical = Margin.verticalLarge)
    ) {
        BarChart(
            barChartData = barChartDataModel.barChartData,
            labelDrawer = barChartDataModel.labelDrawer,
            yAxisDrawer = SimpleYAxisDrawer(labelValueFormatter = { value ->
                "your regex here".format(
                    value
                )
            }
            )
        )
    }
}

@Composable
private fun DrawLabelLocation(
    barChartDataModel: BarChartDataModel,
    newLocation: (DrawLocation) -> Unit
) {
    val labelDrawLocation = remember(barChartDataModel.labelDrawer) {
        barChartDataModel.labelLocation
    }
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(top = Margin.verticalLarge),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(
                    horizontal = Margin.horizontal,
                    vertical = Margin.vertical
                )
                .align(Alignment.CenterVertically),
            horizontalArrangement = Arrangement.SpaceEvenly
        ) {
            for (location in DrawLocation.entries) {
                OutlinedButton(
                    onClick = { newLocation(location) },
                    border = ButtonDefaults.outlinedButtonBorder.takeIf { labelDrawLocation == location },
                ) {
                    Text(text = location.name)
                }
            }
        }
    }
}

@Composable
private fun AddOrRemoveBar(barChartDataModel: BarChartDataModel) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = Margin.vertical),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Center,
    ) {
        Button(
            onClick = { barChartDataModel.removeBar() },
            enabled = barChartDataModel.bars.size > 1,
            shape = CircleShape
        ) {
            Icon(
                imageVector = Icons.Default.Delete,
                contentDescription = "Remove bar from BarChart"
            )
        }

        Row(
            modifier = Modifier.padding(horizontal = Margin.horizontal),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(text = "Bars: ")
            Text(
                text = barChartDataModel.bars.size.toString(),
                style = TextStyle(
                    fontWeight = FontWeight.ExtraBold,
                    fontSize = 18.sp
                )
            )
        }

        Button(
            onClick = { barChartDataModel.addBar() },
            enabled = barChartDataModel.bars.size < 7,
            shape = CircleShape
        ) {
            Icon(
                imageVector = Icons.Default.Add,
                contentDescription = "Add bar into BarChart"
            )
        }
    }
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/screen/line/LineChartDataModel.kt
================================================
package me.bytebeats.views.charts.app.ui.screen.line

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import me.bytebeats.views.charts.line.LineChartData
import me.bytebeats.views.charts.line.LineChartData.Point
import me.bytebeats.views.charts.line.render.point.EmptyPointDrawer
import me.bytebeats.views.charts.line.render.point.FilledCircularPointDrawer
import me.bytebeats.views.charts.line.render.point.HollowCircularPointDrawer
import me.bytebeats.views.charts.line.render.point.IPointDrawer
import kotlin.random.Random

/**
 * Created by bytebeats on 2021/9/30 : 17:49
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
class LineChartDataModel {
    var lineChartData by mutableStateOf(
        LineChartData(
            points = listOf(
                Point(randomYValue(), "Label 1"),
                Point(randomYValue(), "Label 2"),
                Point(randomYValue(), "Label 3"),
                Point(randomYValue(), "Label 4"),
                Point(randomYValue(), "Label 5"),
                Point(randomYValue(), "Label 6"),
                Point(randomYValue(), "Label 7")
            )
        )
    )

    var horizontalOffset by mutableFloatStateOf(5F)

    var pointDrawerType by mutableStateOf(PointDrawerType.Hollow)
    val pointDrawer: IPointDrawer
        get() {
            return when (pointDrawerType) {
                PointDrawerType.None -> EmptyPointDrawer
                PointDrawerType.Filled -> FilledCircularPointDrawer()
                PointDrawerType.Hollow -> HollowCircularPointDrawer()
            }
        }


    private fun randomYValue(): Float = Random.Default.nextInt(45, 145).toFloat()
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/screen/line/LineChartScreen.kt
================================================
package me.bytebeats.views.charts.app.ui.screen.line

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import me.bytebeats.views.charts.app.ui.ScreenRouter
import me.bytebeats.views.charts.app.ui.theme.Margin
import me.bytebeats.views.charts.line.LineChart

/**
 * Created by bytebeats on 2021/9/30 : 17:55
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LineChartScreen() {
    Scaffold(
        topBar = {
            TopAppBar(
                navigationIcon = {
                    IconButton(
                        onClick = { ScreenRouter.navigateHome() }
                    ) {
                        Icon(
                            imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                            contentDescription = "Go back home"
                        )
                    }
                },
                title = { Text(text = "Line Chart") }
            )
        }
    ) { paddingValues ->
        LineChartContent(Modifier.padding(paddingValues))
    }
}

@Composable
private fun LineChartContent(
    modifier: Modifier = Modifier
) {
    val lineChartData = LineChartDataModel()

    Column(
        modifier = modifier.padding(
            horizontal = Margin.horizontal,
            vertical = Margin.vertical
        )
    ) {
        LineChartRow(lineChartDataModel = lineChartData)
        HorizontalOffsetSelector(lineChartDataModel = lineChartData)
        OffsetProgress(lineChartDataModel = lineChartData)
    }
}

@Composable
private fun LineChartRow(lineChartDataModel: LineChartDataModel) {
    Box(
        modifier = Modifier
            .height(250.dp)
            .fillMaxSize()
    ) {
        LineChart(
            lineChartData = lineChartDataModel.lineChartData,
            horizontalOffset = lineChartDataModel.horizontalOffset,
            pointDrawer = lineChartDataModel.pointDrawer
        )
    }
}

@Composable
private fun HorizontalOffsetSelector(lineChartDataModel: LineChartDataModel) {
    val pointDrawType = lineChartDataModel.pointDrawerType
    Column(
        modifier = Modifier.padding(
            horizontal = Margin.horizontal,
            vertical = Margin.vertical
        ),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Point Drawer")
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .align(Alignment.CenterHorizontally)
                .padding(
                    horizontal = Margin.horizontal,
                    vertical = Margin.vertical
                ),
            horizontalArrangement = Arrangement.SpaceEvenly
        ) {
            for (drawerType in PointDrawerType.entries) {
                OutlinedButton(
                    onClick = { lineChartDataModel.pointDrawerType = drawerType },
                    border = ButtonDefaults.outlinedButtonBorder.takeIf { pointDrawType == drawerType },
                ) {
                    Text(text = drawerType.name)
                }
            }
        }
    }
}

@Composable
private fun OffsetProgress(lineChartDataModel: LineChartDataModel) {
    Column(
        modifier = Modifier.padding(horizontal = Margin.horizontal),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Offset")
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(
                    horizontal = Margin.horizontal,
                    vertical = Margin.vertical
                )
                .align(Alignment.CenterHorizontally)
        ) {
            Slider(
                value = lineChartDataModel.horizontalOffset,
                onValueChange = { lineChartDataModel.horizontalOffset = it },
                valueRange = 0F.rangeTo(25F)
            )
        }
    }
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/screen/line/PointDrawerType.kt
================================================
package me.bytebeats.views.charts.app.ui.screen.line

/**
 * Created by bytebeats on 2021/9/30 : 17:48
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
enum class PointDrawerType {
    None,
    Filled,
    Hollow;
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/screen/pie/PieChartDataModel.kt
================================================
package me.bytebeats.views.charts.app.ui.screen.pie

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import me.bytebeats.views.charts.pie.PieChartData
import kotlin.random.Random

/**
 * Created by bytebeats on 2021/9/30 : 12:03
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
class PieChartDataModel {
    private val colors = mutableListOf(
        Color(0XFFF44336),
        Color(0XFFE91E63),
        Color(0XFF9C27B0),
        Color(0XFF673AB7),
        Color(0XFF3F51B5),
        Color(0XFF03A9F4),
        Color(0XFF009688),
        Color(0XFFCDDC39),
        Color(0XFFFFC107),
        Color(0XFFFF5722),
        Color(0XFF795548),
        Color(0XFF9E9E9E),
        Color(0XFF607D8B)
    )

    var sliceThickness by mutableFloatStateOf(25F)

    var pieChartData by mutableStateOf(
        PieChartData(
            slices = listOf(
                PieChartData.Slice(
                    value = randomLength(),
                    color = randomColor()
                ),
                PieChartData.Slice(
                    value = randomLength(),
                    color = randomColor()
                ),
                PieChartData.Slice(
                    value = randomLength(),
                    color = randomColor()
                )
            )
        )
    )

    val slices
        get() = pieChartData.slices

    internal fun addSlice() {
        pieChartData = pieChartData.copy(
            slices = slices.toMutableList().apply {
                add(
                    PieChartData.Slice(
                        value = randomLength(),
                        color = randomColor()
                    )
                )
            }.toList()
        )
    }

    internal fun removeSlice() {
        pieChartData = pieChartData.copy(
            slices = slices.toMutableList().apply {
                val lastSlice = slices.last()
                colors.add(lastSlice.color)
                remove(lastSlice)
            }.toList()
        )
    }

    private fun randomLength(): Float = Random.Default.nextInt(10, 30).toFloat()
    private fun randomColor(): Color {
        val randomIndex = Random.Default.nextInt(colors.size)
        return colors.removeAt(randomIndex)
    }
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/screen/pie/PieChartScreen.kt
================================================
package me.bytebeats.views.charts.app.ui.screen.pie

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import me.bytebeats.views.charts.app.ui.ScreenRouter
import me.bytebeats.views.charts.app.ui.theme.Margin
import me.bytebeats.views.charts.pie.PieChart
import me.bytebeats.views.charts.pie.render.SimpleSliceDrawer

/**
 * Created by bytebeats on 2021/9/30 : 15:50
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PieChartScreen() {
    Scaffold(
        topBar = {
            TopAppBar(
                navigationIcon = {
                    IconButton(
                        onClick = { ScreenRouter.navigateHome() }
                    ) {
                        Icon(
                            imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                            contentDescription = "Go back Home"
                        )
                    }
                },
                title = { Text(text = "Pie Chart") })
        }
    ) { paddingValues ->
        PieChartScreenContent(Modifier.padding(paddingValues))
    }
}

@Composable
private fun PieChartScreenContent(
    modifier: Modifier = Modifier
) {
    val pieChartDataModel = remember {
        PieChartDataModel()
    }

    Column(
        modifier = modifier.padding(
            horizontal = Margin.horizontal,
            vertical = Margin.vertical
        )
    ) {
        PieChartRow(pieChartDataModel = pieChartDataModel)
        SliceThicknessRow(
            sliceThickness = pieChartDataModel.sliceThickness,
            onValueUpdated = { pieChartDataModel.sliceThickness = it },
        )
        AddOrRemoveSliceRow(pieChartDataModel = pieChartDataModel)
    }
}

@Composable
private fun PieChartRow(pieChartDataModel: PieChartDataModel) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .height(150.dp)
            .padding(vertical = Margin.vertical)
    ) {
        PieChart(
            pieChartData = pieChartDataModel.pieChartData,
            sliceDrawer = SimpleSliceDrawer(sliceThickness = pieChartDataModel.sliceThickness)
        )
    }
}

@Composable
private fun SliceThicknessRow(sliceThickness: Float, onValueUpdated: (Float) -> Unit) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = Margin.verticalLarge),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(
            text = "Slice Thickness: ",
            modifier = Modifier
                .align(Alignment.CenterVertically)
                .padding(end = Margin.horizontal)
        )
        Slider(
            value = sliceThickness,
            onValueChange = onValueUpdated,
            valueRange = 10F.rangeTo(100F)
        )
    }
}

@Composable
private fun AddOrRemoveSliceRow(pieChartDataModel: PieChartDataModel) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(top = Margin.vertical),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Center
    ) {
        Button(
            onClick = { pieChartDataModel.removeSlice() },
            enabled = pieChartDataModel.slices.size > 3,
            shape = CircleShape
        ) {
            Icon(
                imageVector = Icons.Filled.Delete,
                contentDescription = "Remove slice from PieChart"
            )
        }
        Row(
            modifier = Modifier.padding(horizontal = Margin.horizontal),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(text = "Slices: ")
            Text(
                text = pieChartDataModel.slices.count().toString(),
                style = TextStyle(
                    fontWeight = FontWeight.ExtraBold,
                    fontSize = 18.sp
                )
            )
        }
        Button(
            onClick = { pieChartDataModel.addSlice() },
            enabled = pieChartDataModel.slices.size < 9,
            shape = CircleShape
        ) {
            Icon(
                imageVector = Icons.Filled.Add,
                contentDescription = "Add Slice to PieChart"
            )
        }
    }
}

@Preview
@Composable
private fun PieChartPreview() = PieChartScreen()


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Color.kt
================================================
package me.bytebeats.views.charts.app.ui.theme

import androidx.compose.ui.graphics.Color

val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Margin.kt
================================================
package me.bytebeats.views.charts.app.ui.theme

import androidx.compose.ui.unit.dp

/**
 * Created by bytebeats on 2021/9/30 : 11:52
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

internal object Margin {
    val horizontal = 15.dp
    val horizontalLarge = 30.dp
    val vertical = 15.dp
    val verticalLarge = 30.dp
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Shape.kt
================================================
package me.bytebeats.views.charts.app.ui.theme

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp

val Shapes = Shapes(
    small = RoundedCornerShape(4.dp),
    medium = RoundedCornerShape(4.dp),
    large = RoundedCornerShape(0.dp)
)


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Theme.kt
================================================
package me.bytebeats.views.charts.app.ui.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable

private val DarkColorPalette = darkColorScheme(
    primary = Purple200,
    primaryContainer = Purple700,
    secondary = Teal200
)

private val LightColorPalette = lightColorScheme(
    primary = Purple500,
    primaryContainer = Purple700,
    secondary = Teal200

    /* Other default colors to override
    background = Color.White,
    surface = Color.White,
    onPrimary = Color.White,
    onSecondary = Color.Black,
    onBackground = Color.Black,
    onSurface = Color.Black,
    */
)

@Composable
fun ComposeChartsTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }

    MaterialTheme(
        colorScheme = colors,
        typography = Typography,
        shapes = Shapes,
        content = content
    )
}


================================================
FILE: app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Type.kt
================================================
package me.bytebeats.views.charts.app.ui.theme

import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

// Set of Material typography styles to start with
val Typography = Typography(
    bodyLarge = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp
    )
    /* Other default text styles to override
    button = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.W500,
        fontSize = 14.sp
    ),
    caption = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 12.sp
    )
    */
)


================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path
        android:fillColor="#3DDC84"
        android:pathData="M0,0h108v108h-108z" />
    <path
        android:fillColor="#00000000"
        android:pathData="M9,0L9,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,0L19,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,0L29,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,0L39,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,0L49,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,0L59,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,0L69,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,0L79,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M89,0L89,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M99,0L99,108"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,9L108,9"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,19L108,19"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,29L108,29"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,39L108,39"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,49L108,49"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,59L108,59"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,69L108,69"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,79L108,79"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,89L108,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,99L108,99"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,29L89,29"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,39L89,39"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,49L89,49"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,59L89,59"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,69L89,69"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,79L89,79"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,19L29,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,19L39,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,19L49,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,19L59,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,19L69,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,19L79,89"
        android:strokeWidth="0.8"
        android:strokeColor="#33FFFFFF" />
</vector>


================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="85.84757"
                android:endY="92.4963"
                android:startX="42.9492"
                android:startY="49.59793"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
        android:strokeWidth="1"
        android:strokeColor="#00000000" />
</vector>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
</resources>

================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
    <string name="app_name">compose-charts</string>
</resources>

================================================
FILE: app/src/main/res/values/themes.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="Theme.Composecharts" parent="android:Theme.Material.Light.NoActionBar">
        <item name="android:statusBarColor">@color/purple_700</item>
    </style>
</resources>

================================================
FILE: build.gradle.kts
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.jetbrains.kotlin.android) apply false
    alias(libs.plugins.android.library) apply false
}

================================================
FILE: charts/.gitignore
================================================
/build/
.idea/
out/

================================================
FILE: charts/build.gradle.kts
================================================
import com.android.build.gradle.LibraryExtension
import org.gradle.jvm.tasks.Jar
import org.jetbrains.dokka.gradle.DokkaTask
import java.net.URI

plugins {
    alias(libs.plugins.android.library)
    alias(libs.plugins.jetbrains.kotlin.android)
    alias(libs.plugins.jetbrains.dokka)
    alias(libs.plugins.detekt.gradle.plugin)
    id("maven-publish")
    id("signing")
}

group = getProperty("GROUP_ID")
version = getProperty("COMPOSE_CHARTS_VERSION")

android {
    namespace = "me.bytebeats.views.charts"
    compileSdk = 34

    defaultConfig {
        minSdk = 24

        lint {
            targetSdk = 34
        }

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles("consumer-rules.pro")
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = libs.versions.ktCompilerExt.get()
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {
    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)

    debugImplementation(libs.androidx.ui.tooling)
}

val sourcesJar by tasks.registering(Jar::class) {
    archiveClassifier.set("sources")

    if (project.plugins.hasPlugin(libs.plugins.android.library.get().pluginId)) {
        val libExt = checkNotNull(project.extensions.findByType(LibraryExtension::class.java))
        val libMainSourceSet = libExt.sourceSets.getByName("main")

        from(libMainSourceSet.java.srcDirs)
    } else {
        val sourceSetExt =
            checkNotNull(project.extensions.findByType(SourceSetContainer::class.java))
        val mainSourceSet = sourceSetExt.getByName("main")

        from(mainSourceSet.java.srcDirs)
    }
}

tasks.withType(GenerateModuleMetadata::class).configureEach {
    dependsOn(sourcesJar)
}

tasks.dokkaHtml {
    outputDirectory.set(layout.buildDirectory.dir("dokka"))
    moduleName.set(getProperty("MODULE_NAME"))
    dokkaSourceSets {
        configureEach {
            suppress = false
            offlineMode = false
            includeNonPublic = false
            skipDeprecated = true
            skipEmptyPackages = true
            noStdlibLink = true
            noJdkLink = true
            noAndroidSdkLink = false
            jdkVersion = JavaVersion.VERSION_1_8.ordinal + 1
        }
    }
}

val dokkaHtml by tasks.getting(DokkaTask::class)

val javadocJar by tasks.registering(Jar::class) {
    dependsOn(dokkaHtml)
    archiveClassifier.set("javadoc")
    from(dokkaHtml.outputDirectory)
}

fun Project.getProperty(key: String?, default: String? = null): String {
    checkPropertyKey(key)
    return properties[key]?.toString() ?: System.getProperty(key!!, default)
}

fun checkPropertyKey(key: String?) {
    if (key == null) {
        throw NullPointerException("key can't be null")
    }
    if (key.isBlank()) {
        throw IllegalArgumentException("key can't be blank")
    }
}

fun Project.checkSigningKey(signingKey: String?) {
    checkPropertyKey(signingKey)
    signingKey?.let { key ->
        if (hasProperty(key).not() && System.getProperties().containsKey(key).not()) {
            throw IllegalStateException("$signingKey has to be declared in local.properties or ~/.gradle/gradle.properties")
        }
    }
}

fun Project.getRepoUrl(): URI {
    val isSnapshot = getProperty("COMPOSE_CHARTS_VERSION").contains("SNAPSHOT")
    val releaseUrl = getProperty(
        "RELEASES_REPO_URL",
        "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
    )
    val snapshotUrl = getProperty(
        "SNAPSHOTS_REPO_URL",
        "https://s01.oss.sonatype.org/content/repositories/snapshots/"
    )
    return uri(if (isSnapshot) snapshotUrl else releaseUrl)
}

afterEvaluate {
    publishing {
        publications {
            // 1. configure repositories
            repositories {
                maven {
                    name = project.getProperty("REPO_NAME")
                    url = project.getRepoUrl()

                    credentials {
                        username = project.getProperty("ossrhUsername", "")
                        password = project.getProperty("ossrhPassword", "")
                    }
                }
            }

            // 2. configure publication
            val publicationName = project.getProperty("PUBLICATION_NAME", "release")
            create<MavenPublication>(publicationName) {

                if (project.plugins.hasPlugin(libs.plugins.android.library.get().pluginId)) {
                    from(components["release"])
                } else {
                    from(components["java"])
                }

                artifact(sourcesJar.get())
                artifact(javadocJar.get())

                pom {
                    groupId = project.getProperty("GROUP_ID")
                    artifactId = project.getProperty("COMPOSE_CHARTS_ARTIFACT_ID")
                    version = project.getProperty("COMPOSE_CHARTS_VERSION")
                    inceptionYear = project.getProperty("COMPOSE_CHARTS_INCEPTION_YEAR")

                    name = project.getProperty("MODULE_NAME")
                    description = project.getProperty("COMPOSE_CHARTS_DESCRIPTION")
                    url = project.getProperty("COMPOSE_CHARTS_URL")

                    packaging = project.getProperty("COMPOSE_CHARTS_PACKAGING")

                    scm {
                        url = project.getProperty("SCM_URL")
                        connection = project.getProperty("SCM_CONNECTION")
                        developerConnection = project.getProperty("SCM_DEVELOPER_CONNECTION")
                    }

                    organization {
                        name = project.getProperty("ORGANIZATION_NAME", "")
                        url = project.getProperty("ORGANIZATION_URL", "")
                    }

                    developers {
                        developer {
                            id = project.getProperty("DEVELOPER_ID")
                            name = project.getProperty("DEVELOPER_NAME")
                            url = project.getProperty("DEVELOPER_URL")
                            email = project.getProperty("DEVELOPER_EMAIL")
                        }
                    }

                    licenses {
                        license {
                            name = project.getProperty("LICENSE_NAME")
                            url = project.getProperty("LICENSE_URL")
                            distribution = project.getProperty("LICENCE_DIST")
                        }
                    }

                    issueManagement {
                        system = project.getProperty("ISSUE_SYSTEM")
                        url = project.getProperty("ISSUE_URL")
                    }

                    contributors {
                        contributor {
                            name = project.getProperty("CONTRIBUTOR_NAME")
                            email = project.getProperty("CONTRIBUTOR_EMAIL")
                            url = project.getProperty("CONTRIBUTOR_URL")
                            roles.set(listOf("Master", "Maintainer", "Developer"))
                            timezone = project.getProperty("CONTRIBUTOR_TIMEZONE")
                        }
                    }

                    ciManagement {
                        system = project.getProperty("CI_SYSTEM")
                        url = project.getProperty("CI_URL")
                    }

                    distributionManagement {
                        downloadUrl = getProperty("RELEASES_REPO_URL")
                    }
                }
            }

            // 3. sign the artifacts
            signing {
                // Choose one of both ways to sign the aar
                // Signing with gpg
                // and with its signing.keyId & signing.password & signing.secretKeyRingFile
                // declared in local.properties or ~/.gradle/gradle.properties
//                checkSigningKey("signing.keyId")
//                checkSigningKey("signing.password")
//                checkSigningKey("signing.secretKeyRingFile")
                sign(publishing.publications.getByName(publicationName))
                // or signing with CI/CD
                // and with its signingKeyId & signingKeyPassword & signingKey
                // declared in local.properties or ~/.gradle/gradle.properties
//                checkSigningKey("signingKeyId")
//                checkSigningKey("signingKey")
//                checkSigningKey("signingKeyPassword")
//                val signingKeyId = getProperty("signingKeyId")
//                val signingKey = getProperty("signingKey")
//                val signingKeyPassword = getProperty("signingKeyPassword")
//                useInMemoryPgpKeys(signingKeyId, signingKey, signingKeyPassword)
//                sign(publishing.publications.getByName(publicationName))
            }
        }
    }
}


================================================
FILE: charts/consumer-rules.pro
================================================


================================================
FILE: charts/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

================================================
FILE: charts/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest />

================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/Animations.kt
================================================
package me.bytebeats.views.charts

import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.TweenSpec

/**
 * Created by bytebeats on 2021/9/24 : 10:53
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

fun simpleChartAnimation(): AnimationSpec<Float> = TweenSpec(durationMillis = 500)


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/AxisLine.kt
================================================
package me.bytebeats.views.charts

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

/**
 * Created by bytebeats on 2021/9/24 : 10:47
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class AxisLine(
    val thickness: Dp = 1.5.dp,
    val color: Color = Color.Gray
) {
    private val mPaint by lazy {
        Paint().apply {
            color = this@AxisLine.color
            style = PaintingStyle.Stroke
        }
    }

    @Suppress("UndocumentedPublicFunction")
    fun paint(density: Density) {
        mPaint.strokeWidth = thickness.value * density.density
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/Colors.kt
================================================
package me.bytebeats.views.charts

import androidx.compose.ui.graphics.Color
import me.bytebeats.views.charts.util.FLOAT_0_5
import me.bytebeats.views.charts.util.FLOAT_255

/**
 * Created by bytebeats on 2021/9/24 : 10:51
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

fun Color.toLegacyInt(): Int {
    return android.graphics.Color.argb(
        (alpha * FLOAT_255 + FLOAT_0_5).toInt(),
        (red * FLOAT_255 + FLOAT_0_5).toInt(),
        (green * FLOAT_255 + FLOAT_0_5).toInt(),
        (blue * FLOAT_255 + FLOAT_0_5).toInt()
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/TypeAlias.kt
================================================
package me.bytebeats.views.charts

/**
 * Created by bytebeats on 2021/9/25 : 15:37
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

typealias LabelFormatter = (value: Float) -> String
typealias AxisLabelFormatter = (value: Any?) -> String


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/BarChart.kt
================================================
package me.bytebeats.views.charts.bar

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.draw.drawBehind
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import me.bytebeats.views.charts.bar.render.bar.IBarDrawer
import me.bytebeats.views.charts.bar.render.bar.SimpleBarDrawer
import me.bytebeats.views.charts.bar.render.label.ILabelDrawer
import me.bytebeats.views.charts.bar.render.label.SimpleLabelDrawer
import me.bytebeats.views.charts.bar.render.xaxis.IXAxisDrawer
import me.bytebeats.views.charts.bar.render.xaxis.SimpleXAxisDrawer
import me.bytebeats.views.charts.bar.render.yaxis.IYAxisDrawer
import me.bytebeats.views.charts.bar.render.yaxis.SimpleYAxisDrawer
import me.bytebeats.views.charts.simpleChartAnimation

/**
 * Created by bytebeats on 2021/9/25 : 15:56
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

@Composable
fun BarChart(
    barChartData: BarChartData,
    modifier: Modifier = Modifier,
    animation: AnimationSpec<Float> = simpleChartAnimation(),
    barDrawer: IBarDrawer = SimpleBarDrawer(),
    xAxisDrawer: IXAxisDrawer = SimpleXAxisDrawer(),
    yAxisDrawer: IYAxisDrawer = SimpleYAxisDrawer(),
    labelDrawer: ILabelDrawer = SimpleLabelDrawer()
) {
    val transitionAnimation = remember(barChartData.bars) {
        Animatable(initialValue = 0F)
    }

    LaunchedEffect(barChartData.bars) {
        transitionAnimation.animateTo(1F, animationSpec = animation)
    }

    val progress = transitionAnimation.value
    Canvas(
        modifier = modifier
            .fillMaxSize()
            .drawBehind {
                drawIntoCanvas { canvas ->
                    val (xAxisArea, yAxisArea) = axisAreas(
                        drawScope = this,
                        totalSize = size,
                        xAxisDrawer = xAxisDrawer,
                        labelDrawer = labelDrawer
                    )

                    val barDrawableArea = barDrawableArea(xAxisArea)

                    yAxisDrawer.drawAxisLine(
                        drawScope = this,
                        canvas = canvas,
                        drawableArea = yAxisArea
                    )

                    xAxisDrawer.drawXAxisLine(
                        drawScope = this,
                        canvas = canvas,
                        drawableArea = xAxisArea
                    )

                    barChartData.forEachWithArea(
                        this,
                        barDrawableArea,
                        progress,
                        labelDrawer
                    ) { barArea, bar ->
                        barDrawer.drawBar(drawScope = this, canvas, barArea, bar)
                    }
                }
            }
    ) {

        drawIntoCanvas { canvas ->
            val (xAxisArea, yAxisArea) = axisAreas(
                drawScope = this,
                totalSize = size,
                xAxisDrawer = xAxisDrawer,
                labelDrawer = labelDrawer
            )
            val barDrawableArea = barDrawableArea(xAxisArea)

            barChartData.forEachWithArea(
                this,
                barDrawableArea,
                progress,
                labelDrawer
            ) { barArea, bar ->
                labelDrawer.drawLabel(
                    drawScope = this,
                    canvas = canvas,
                    label = bar.label,
                    barArea = barArea,
                    xAxisArea = xAxisArea
                )
            }

            yAxisDrawer.drawAxisLabels(
                drawScope = this,
                canvas = canvas,
                minValue = barChartData.minY,
                maxValue = barChartData.maxY,
                drawableArea = yAxisArea
            )
        }
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/BarChartData.kt
================================================
package me.bytebeats.views.charts.bar

import androidx.compose.ui.graphics.Color
import me.bytebeats.views.charts.util.FLOAT_100

/**
 * Created by bytebeats on 2021/9/25 : 13:52 E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class BarChartData(
    val bars: List<Bar>,
    val padBy: Float = 10F,
    val startAtZero: Boolean = true,
    val maxBarValue: Float = bars.maxOf { it.value }
) {

    init {
        require(padBy in 0F..FLOAT_100) {
            "padBy must be between 0F and 100F, included"
        }
        require(maxBarValue >= bars.maxOf { it.value }) {
            "maxBarValue must be at least the value of the highest bar"
        }
    }

    private val yMinMaxValues: Pair<Float, Float>
        get() {
            val minValue = bars.minOf { it.value }
            val maxValue = maxBarValue
            return minValue to maxValue
        }

    val maxY: Float
        get() = yMinMaxValues.second + (yMinMaxValues.second - yMinMaxValues.first) * padBy / FLOAT_100
    val minY: Float
        get() = if (startAtZero) 0F
        else yMinMaxValues.first - (yMinMaxValues.second - yMinMaxValues.first) * padBy / FLOAT_100

    data class Bar(
        val value: Float,
        val color: Color,
        val label: String
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/BarCharts.kt
================================================
package me.bytebeats.views.charts.bar

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.dp
import me.bytebeats.views.charts.bar.render.label.ILabelDrawer
import me.bytebeats.views.charts.bar.render.xaxis.IXAxisDrawer
import me.bytebeats.views.charts.util.FLOAT_10
import me.bytebeats.views.charts.util.FLOAT_100

/**
 * Created by bytebeats on 2021/9/25 : 13:57
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

internal fun axisAreas(
    drawScope: DrawScope,
    totalSize: Size,
    xAxisDrawer: IXAxisDrawer,
    labelDrawer: ILabelDrawer
): Pair<Rect, Rect> {
    with(drawScope) {

        val yAxisTop = labelDrawer.requiredAboveBarHeight(drawScope)
        val yAxisRight = 50.dp.toPx().coerceAtMost(size.width * FLOAT_10 / FLOAT_100)
        val xAxisRight = totalSize.width
        val xAxisTop = totalSize.height - xAxisDrawer.requiredHeight(drawScope)

        return Rect(
            left = yAxisRight,
            top = xAxisTop,
            right = xAxisRight,
            bottom = totalSize.height
        ) to Rect(
            left = 0F,
            top = yAxisTop,
            right = yAxisRight,
            bottom = xAxisTop
        )
    }
}

internal fun barDrawableArea(xAxisArea: Rect): Rect =
    Rect(
        left = xAxisArea.left,
        top = 0F,
        right = xAxisArea.right,
        bottom = xAxisArea.top
    )

internal fun BarChartData.forEachWithArea(
    drawScope: DrawScope,
    barDrawableArea: Rect,
    progress: Float,
    labelDrawer: ILabelDrawer,
    block: (barArea: Rect, bar: BarChartData.Bar) -> Unit
) {
    val barCount = bars.size
    val widthOfBarArea = barDrawableArea.width / barCount
    val offsetOfBar = widthOfBarArea * 0.2F

    bars.forEachIndexed { index, bar ->
        val left = barDrawableArea.left + index * widthOfBarArea
        val height = barDrawableArea.height
        val barHeight = (height - labelDrawer.requiredAboveBarHeight(drawScope)) * progress
        val barArea = Rect(
            left = left + offsetOfBar,
            top = barDrawableArea.bottom - bar.value / maxBarValue * barHeight,
            right = left + widthOfBarArea - offsetOfBar,
            bottom = barDrawableArea.bottom
        )
        block(barArea, bar)
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/render/bar/IBarDrawer.kt
================================================
package me.bytebeats.views.charts.bar.render.bar

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope
import me.bytebeats.views.charts.bar.BarChartData

/**
 * Created by bytebeats on 2021/9/25 : 15:53
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface IBarDrawer {

    /**
     * Draw bar
     *
     * @param drawScope the scope to draw
     * @param canvas the Canvas to draw on
     * @param barArea the bar area to draw
     * @param bar the bar data
     */
    fun drawBar(
        drawScope: DrawScope,
        canvas: Canvas,
        barArea: Rect,
        bar: BarChartData.Bar
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/render/bar/SimpleBarDrawer.kt
================================================
package me.bytebeats.views.charts.bar.render.bar

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.drawscope.DrawScope
import me.bytebeats.views.charts.bar.BarChartData

/**
 * Created by bytebeats on 2021/9/25 : 15:54
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
class SimpleBarDrawer : IBarDrawer {
    private val mBarPaint by lazy { Paint().apply { isAntiAlias = true } }

    override fun drawBar(
        drawScope: DrawScope,
        canvas: Canvas,
        barArea: Rect,
        bar: BarChartData.Bar
    ) {
        canvas.drawRect(barArea, mBarPaint.apply { color = bar.color })
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/render/label/ILabelDrawer.kt
================================================
package me.bytebeats.views.charts.bar.render.label

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/25 : 13:59
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface ILabelDrawer {


    /**
     * Required x axis height
     *
     * @param drawScope the scope to draw in
     * @return required height
     */
    fun requiredXAxisHeight(drawScope: DrawScope): Float = 0F


    /**
     * Required above bar height
     *
     * @param drawScope the scope to draw in
     * @return required height
     */
    fun requiredAboveBarHeight(drawScope: DrawScope): Float = 0F

    /**
     * Draw label
     *
     * @param drawScope the scope to draw in
     * @param canvas the canvas to draw on
     * @param label the label to draw on the axis
     * @param barArea the area to draw a bar
     * @param xAxisArea the x axis area to draw
     */
    fun drawLabel(
        drawScope: DrawScope,
        canvas: Canvas,
        label: Any?,
        barArea: Rect,
        xAxisArea: Rect
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/render/label/SimpleLabelDrawer.kt
================================================
package me.bytebeats.views.charts.bar.render.label

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp
import me.bytebeats.views.charts.AxisLabelFormatter
import me.bytebeats.views.charts.toLegacyInt
import me.bytebeats.views.charts.util.FLOAT_1_5

/**
 * Created by bytebeats on 2021/9/25 : 14:01
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class SimpleLabelDrawer(
    val drawLocation: DrawLocation = DrawLocation.Inside,
    val labelTextSize: TextUnit = 12.sp,
    val labelTextColor: Color = Color.Black,
    val axisLabelFormatter: AxisLabelFormatter = { value -> "$value" }
) : ILabelDrawer {
    private val mLabelTextArea: Float? = null
    private val mPaint by lazy {
        android.graphics.Paint().apply {
            textAlign = android.graphics.Paint.Align.CENTER
            color = labelTextColor.toLegacyInt()
        }
    }

    override fun requiredAboveBarHeight(drawScope: DrawScope): Float = when (drawLocation) {
        DrawLocation.Outside -> FLOAT_1_5 * labelTextHeight(drawScope)
        else -> 0F
    }

    override fun requiredXAxisHeight(drawScope: DrawScope): Float = when (drawLocation) {
        DrawLocation.XAxis -> labelTextHeight(drawScope)
        else -> 0F
    }

    override fun drawLabel(
        drawScope: DrawScope,
        canvas: Canvas,
        label: Any?,
        barArea: Rect,
        xAxisArea: Rect
    ) {
        with(drawScope) {
            val xCenter = barArea.left + barArea.width / 2
            val yCenter = when (drawLocation) {
                DrawLocation.Inside -> (barArea.top + barArea.bottom) / 2
                DrawLocation.Outside -> barArea.top - labelTextSize.toPx() / 2
                DrawLocation.XAxis -> barArea.bottom + labelTextHeight(drawScope)
            }
            val labelValue = axisLabelFormatter(label)
            canvas.nativeCanvas.drawText(labelValue, xCenter, yCenter, paint(drawScope))
        }
    }

    private fun labelTextHeight(drawScope: DrawScope): Float = with(drawScope) {
        mLabelTextArea ?: (FLOAT_1_5 * labelTextSize.toPx())
    }

    private fun paint(drawScope: DrawScope): android.graphics.Paint = with(drawScope) {
        mPaint.apply { textSize = labelTextSize.toPx() }
    }

    enum class DrawLocation {
        Inside,
        Outside,
        XAxis;
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/render/xaxis/IXAxisDrawer.kt
================================================
package me.bytebeats.views.charts.bar.render.xaxis

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/25 : 14:16
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface IXAxisDrawer {

    /**
     * Required height
     *
     * @param drawScope the draw scope to require height
     * @return required height
     */
    fun requiredHeight(drawScope: DrawScope): Float

    /**
     * Draw x axis line
     *
     * @param drawScope the scope to draw
     * @param canvas the canvas to draw on
     * @param drawableArea the area to draw a drawable
     */
    fun drawXAxisLine(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/render/xaxis/SimpleXAxisDrawer.kt
================================================
package me.bytebeats.views.charts.bar.render.xaxis

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import me.bytebeats.views.charts.util.FLOAT_1_5

/**
 * Created by bytebeats on 2021/9/25 : 14:18
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class SimpleXAxisDrawer(
    val axisLineThickness: Dp = 1.dp,
    val axisLineColor: Color = Color.Black
) : IXAxisDrawer {

    private val mPaint by lazy {
        Paint().apply {
            isAntiAlias = true
            color = axisLineColor
            style = PaintingStyle.Stroke
        }
    }

    override fun requiredHeight(drawScope: DrawScope): Float = with(drawScope) {
        FLOAT_1_5 * axisLineThickness.toPx()
    }

    override fun drawXAxisLine(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect
    ) {
        with(drawScope) {
            val lineThickness = axisLineThickness.toPx()
            val y = drawableArea.top + lineThickness / 2F
            canvas.drawLine(
                p1 = Offset(x = drawableArea.left, y = y),
                p2 = Offset(x = drawableArea.right, y = y),
                paint = mPaint.apply {
                    strokeWidth = lineThickness
                })
        }
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/render/yaxis/IYAxisDrawer.kt
================================================
package me.bytebeats.views.charts.bar.render.yaxis

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/25 : 14:26
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface IYAxisDrawer {

    /**
     * Draw axis line
     *
     * @param drawScope the scope to draw
     * @param canvas the canvas to draw on
     * @param drawableArea the area to draw a drawable
     */
    fun drawAxisLine(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect
    )


    /**
     * Draw axis labels
     *
     * @param drawScope the scope to draw
     * @param canvas the canvas to draw on
     * @param drawableArea the drawable area
     * @param minValue the min value of the y axis data
     * @param maxValue the max value of the y axis data
     */
    fun drawAxisLabels(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect,
        minValue: Float,
        maxValue: Float
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/bar/render/yaxis/SimpleYAxisDrawer.kt
================================================
package me.bytebeats.views.charts.bar.render.yaxis

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import me.bytebeats.views.charts.LabelFormatter
import me.bytebeats.views.charts.toLegacyInt
import kotlin.math.roundToInt

/**
 * Created by bytebeats on 2021/9/25 : 14:27
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

data class SimpleYAxisDrawer(
    val labelTextSize: TextUnit = 12.sp,
    val labelTextColor: Color = Color.Black,
    val drawLabelEvery: Int = 3,
    val labelValueFormatter: LabelFormatter = { value -> "%.1f".format(value) },
    val axisLineThickness: Dp = 1.dp,
    val axisLineColor: Color = Color.Black
) : IYAxisDrawer {

    private val mAxisLinePaint by lazy {
        Paint().apply {
            isAntiAlias = true
            color = axisLineColor
            style = PaintingStyle.Stroke
        }
    }

    private val mTextPaint by lazy {
        android.graphics.Paint().apply {
            isAntiAlias = true
            color = labelTextColor.toLegacyInt()
        }
    }

    private val mTextBounds = android.graphics.Rect()

    override fun drawAxisLine(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect
    ) {
        with(drawScope) {
            val lineThickness = axisLineThickness.toPx()
            val x = drawableArea.right - lineThickness / 2F
            canvas.drawLine(
                p1 = Offset(x = x, y = drawableArea.top),
                p2 = Offset(x = x, y = drawableArea.bottom),
                paint = mAxisLinePaint.apply { strokeWidth = lineThickness }
            )
        }
    }

    override fun drawAxisLabels(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect,
        minValue: Float,
        maxValue: Float
    ) {
        with(drawScope) {
            val labelPaint = mTextPaint.apply {
                textSize = labelTextSize.toPx()
                textAlign = android.graphics.Paint.Align.RIGHT
            }
            val minLabelHeight = labelTextSize.toPx() * drawLabelEvery.toFloat()
            val totalHeight = drawableArea.height
            val labelCount = (drawableArea.height / minLabelHeight).roundToInt().coerceAtLeast(2)
            for (i in 0..labelCount) {
                val value = minValue + i * (maxValue - minValue) / labelCount
                val label = labelValueFormatter(value)
                val x = drawableArea.right - axisLineThickness.toPx() - labelTextSize.toPx() / 2F
                labelPaint.getTextBounds(label, 0, label.length, mTextBounds)
                val y =
                    drawableArea.bottom - i * (totalHeight / labelCount) + mTextBounds.height() / 2F
                canvas.nativeCanvas.drawText(label, x, y, labelPaint)
            }
        }
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/LineChart.kt
================================================
package me.bytebeats.views.charts.line

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 me.bytebeats.views.charts.line.render.line.EmptyLineShader
import me.bytebeats.views.charts.line.render.line.ILineDrawer
import me.bytebeats.views.charts.line.render.line.ILineShader
import me.bytebeats.views.charts.line.render.line.SolidLineDrawer
import me.bytebeats.views.charts.line.render.point.FilledCircularPointDrawer
import me.bytebeats.views.charts.line.render.point.IPointDrawer
import me.bytebeats.views.charts.line.render.xaxis.IXAxisDrawer
import me.bytebeats.views.charts.line.render.xaxis.SimpleXAxisDrawer
import me.bytebeats.views.charts.line.render.yaxis.IYAxisDrawer
import me.bytebeats.views.charts.line.render.yaxis.SimpleYAxisDrawer
import me.bytebeats.views.charts.simpleChartAnimation

/**
 * Created by bytebeats on 2021/9/25 : 12:55
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

@Composable
fun LineChart(
    lineChartData: LineChartData,
    modifier: Modifier = Modifier,
    animation: AnimationSpec<Float> = simpleChartAnimation(),
    pointDrawer: IPointDrawer = FilledCircularPointDrawer(),
    lineDrawer: ILineDrawer = SolidLineDrawer(),
    lineShader: ILineShader = EmptyLineShader,
    xAxisDrawer: IXAxisDrawer = SimpleXAxisDrawer(),
    yAxisDrawer: IYAxisDrawer = SimpleYAxisDrawer(),
    horizontalOffset: Float = 5F,
) {
    check(horizontalOffset in 0F..25F) {
        "Horizontal Offset is the percentage offset from side, and must be between 0 and 25, included."
    }
    val transitionAnimation = remember(lineChartData.points) {
        Animatable(initialValue = 0F)
    }

    LaunchedEffect(lineChartData.points) {
        transitionAnimation.snapTo(0F)
        transitionAnimation.animateTo(1F, animationSpec = animation)
    }

    Canvas(
        modifier = modifier.fillMaxSize()
    ) {
        drawIntoCanvas { canvas ->
            val yAxisDrawableArea = computeYAxisDrawableArea(
                xAxisLabelSize = xAxisDrawer.requireHeight(this),
                size = size
            )
            val xAxisDrawableArea = computeXAxisDrawableArea(
                yAxisWidth = yAxisDrawableArea.width,
                labelHeight = xAxisDrawer.requireHeight(this),
                size = size
            )
            val xAxisLabelsDrawableArea = computeXAxisLabelsDrawableArea(
                xAxisDrawableArea = xAxisDrawableArea,
                offset = horizontalOffset
            )

            val chartDrawableArea = computeDrawableArea(
                xAxisDrawableArea = xAxisDrawableArea,
                yAxisDrawableArea = yAxisDrawableArea,
                size = size,
                offset = horizontalOffset
            )

            lineDrawer.drawLine(
                drawScope = this,
                canvas = canvas,
                linePath = computeLinePath(
                    drawableArea = chartDrawableArea,
                    lineChartData = lineChartData,
                    transitionProgress = transitionAnimation.value
                )
            )
            lineShader.fillLine(
                drawScope = this,
                canvas = canvas,
                fillPath = computeFillPath(
                    drawableArea = chartDrawableArea,
                    lineChartData = lineChartData,
                    transitionProgress = transitionAnimation.value
                )
            )
            lineChartData.points.forEachIndexed { index, point ->
                withProgress(
                    index = index,
                    lineChartData = lineChartData,
                    transitionProgress = transitionAnimation.value
                ) {
                    pointDrawer.drawPoint(
                        drawScope = this,
                        canvas = canvas,
                        center = computePointLocation(
                            drawableArea = chartDrawableArea,
                            lineChartData = lineChartData,
                            point = point,
                            index = index
                        )
                    )
                }
            }

            xAxisDrawer.drawXAxisLine(
                drawScope = this,
                drawableArea = xAxisDrawableArea,
                canvas = canvas
            )
            xAxisDrawer.drawXAxisLabels(
                drawScope = this,
                canvas = canvas,
                drawableArea = xAxisLabelsDrawableArea,
                labels = lineChartData.points.map { it.label })
            yAxisDrawer.drawAxisLine(
                drawScope = this,
                drawableArea = yAxisDrawableArea,
                canvas = canvas
            )
            yAxisDrawer.drawAxisLabels(
                drawScope = this,
                canvas = canvas,
                drawableArea = yAxisDrawableArea,
                minValue = lineChartData.minY,
                maxValue = lineChartData.maxY
            )
        }
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/LineChartData.kt
================================================
package me.bytebeats.views.charts.line

import me.bytebeats.views.charts.util.FLOAT_100

/**
 * Created by bytebeats on 2021/9/24 : 19:39
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class LineChartData(
    val points: List<Point>,
    val padBy: Float = 20F,// percentage we pad yValue by
    val startAtZero: Boolean = false
) {
    init {
        require(padBy in 0F..FLOAT_100) {
            "padBy must be between 0F and 100F, included"
        }
    }

    private val yMinMaxValues: Pair<Float, Float>
        get() {
            val minValue = points.minOf { it.value }
            val maxValue = points.maxOf { it.value }
            return minValue to maxValue
        }

    val maxY: Float
        get() = yMinMaxValues.second + (yMinMaxValues.second - yMinMaxValues.first) * padBy / FLOAT_100
    val minY: Float
        get() = if (startAtZero) 0F
        else yMinMaxValues.first - (yMinMaxValues.second - yMinMaxValues.first) * padBy / FLOAT_100

    val yRange: Float
        get() = maxY - minY

    data class Point(
        val value: Float,
        val label: String
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/LineCharts.kt
================================================
package me.bytebeats.views.charts.line

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import me.bytebeats.views.charts.util.FLOAT_10
import me.bytebeats.views.charts.util.FLOAT_100

/**
 * Created by bytebeats on 2021/9/24 : 19:26
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

internal fun computeDrawableArea(
    xAxisDrawableArea: Rect,
    yAxisDrawableArea: Rect,
    size: Size,
    offset: Float
): Rect {
    val horizontalOffset = xAxisDrawableArea.width * offset / FLOAT_100
    return Rect(
        left = yAxisDrawableArea.right + horizontalOffset,
        top = 0F,
        bottom = xAxisDrawableArea.top,
        right = size.width - horizontalOffset
    )
}

internal fun computeXAxisDrawableArea(
    yAxisWidth: Float,
    labelHeight: Float,
    size: Size
): Rect {
    val top = size.height - labelHeight
    return Rect(
        left = yAxisWidth,
        top = top,
        right = size.width,
        bottom = size.height
    )
}

internal fun computeXAxisLabelsDrawableArea(
    xAxisDrawableArea: Rect,
    offset: Float
): Rect {
    val horizontalOffset = xAxisDrawableArea.width * offset / FLOAT_100
    return Rect(
        left = xAxisDrawableArea.left + horizontalOffset,
        top = xAxisDrawableArea.top,
        right = xAxisDrawableArea.right - horizontalOffset,
        bottom = xAxisDrawableArea.bottom
    )
}

internal fun Density.computeYAxisDrawableArea(
    xAxisLabelSize: Float,
    size: Size
): Rect {
    val right =
        50.dp.toPx().coerceAtMost(size.width * FLOAT_10 / FLOAT_100) // 50dp or 10% of chart view width
    return Rect(
        left = 0F,
        top = 0F,
        right = right,
        bottom = size.height - xAxisLabelSize
    )
}

internal fun computePointLocation(
    drawableArea: Rect,
    lineChartData: LineChartData,
    point: LineChartData.Point,
    index: Int
): Offset {
    val dx = index.toFloat() / (lineChartData.points.size - 1)
    val dy = (point.value - lineChartData.minY) / lineChartData.yRange
    return Offset(
        x = dx * drawableArea.width + drawableArea.left,
        y = drawableArea.height - dy * drawableArea.height
    )
}

internal fun withProgress(
    index: Int,
    lineChartData: LineChartData,
    transitionProgress: Float,
    progressListener: (progress: Float) -> Unit
) {
    val size = lineChartData.points.size
    val toIndex = (size * transitionProgress).toInt() + 1
    if (index == toIndex) {
        val sizeF = lineChartData.points.size.toFloat()
        val divider = 1F / sizeF
        val down = (index - 1) * divider
        progressListener((transitionProgress - down) / divider)
    } else if (index < toIndex) {
        progressListener(1F)
    }
}

internal fun computeLinePath(
    drawableArea: Rect,
    lineChartData: LineChartData,
    transitionProgress: Float
): Path = Path().apply {
    var prePointLocation: Offset? = null
    lineChartData.points.forEachIndexed { index, point ->
        withProgress(index, lineChartData, transitionProgress) { progress ->
            val pointLocation = computePointLocation(drawableArea, lineChartData, point, index)
            if (index == 0) {
                moveTo(pointLocation.x, pointLocation.y)
            } else {
                if (progress <= 1F) {
                    val preX = prePointLocation?.x ?: 0F
                    val preY = prePointLocation?.y ?: 0F
                    val tx = (pointLocation.x - preX) * progress + preX
                    val ty = (pointLocation.y - preY) * progress + preY
                    lineTo(tx, ty)
                } else {
                    lineTo(pointLocation.x, pointLocation.y)
                }
            }
            prePointLocation = pointLocation
        }
    }
}

internal fun computeFillPath(
    drawableArea: Rect,
    lineChartData: LineChartData,
    transitionProgress: Float
): Path = Path().apply {
    moveTo(drawableArea.left, drawableArea.bottom)
    var prePointX: Float? = null
    var prePointLocation: Offset? = null
    lineChartData.points.forEachIndexed { index, point ->
        withProgress(index, lineChartData, transitionProgress) { progress ->
            val pointLocation = computePointLocation(drawableArea, lineChartData, point, index)
            if (index == 0) {
                lineTo(drawableArea.left, pointLocation.y)
                lineTo(pointLocation.x, pointLocation.y)
            } else {
                prePointX = if (progress <= 1F) {
                    val preX = prePointLocation?.x ?: 0F
                    val preY = prePointLocation?.y ?: 0F
                    val tx = (pointLocation.x - preX) * progress + preX
                    val ty = (pointLocation.y - preY) * progress + preY
                    lineTo(tx, ty)
                    tx
                } else {
                    lineTo(pointLocation.x, pointLocation.y)
                    pointLocation.x
                }
            }
            prePointLocation = pointLocation
        }
    }
    prePointX?.let {
        lineTo(it, drawableArea.bottom)
        lineTo(drawableArea.left, drawableArea.bottom)
    } ?: lineTo(drawableArea.left, drawableArea.bottom)
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/line/EmptyLineShader.kt
================================================
package me.bytebeats.views.charts.line.render.line

import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/25 : 12:50
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
object EmptyLineShader : ILineShader {
    override fun fillLine(
        drawScope: DrawScope,
        canvas: Canvas,
        fillPath: Path
    ) {
        // do nothing here
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/line/GradientLineShader.kt
================================================
package me.bytebeats.views.charts.line.render.line

import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/25 : 12:52
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class GradientLineShader(
    val colors: List<Color> = listOf(Color.Blue, Color.Transparent)
) : ILineShader {

    private val mBrush = Brush.verticalGradient(colors)

    override fun fillLine(
        drawScope: DrawScope,
        canvas: Canvas,
        fillPath: Path
    ) {
        drawScope.drawPath(path = fillPath, brush = mBrush)
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/line/ILineDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.line

import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/25 : 12:45
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface ILineDrawer {

    /**
     * Draw line
     *
     * @param drawScope the scope to draw in
     * @param canvas the canvas to draw on
     * @param linePath the line path to draw
     */
    fun drawLine(
        drawScope: DrawScope,
        canvas: Canvas,
        linePath: Path
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/line/ILineShader.kt
================================================
package me.bytebeats.views.charts.line.render.line

import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/25 : 12:49
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface ILineShader {

    /**
     * fill slice
     *
     * @param drawScope the scope to draw in
     * @param canvas the canvas to draw on
     * @param fillPath the path to fill
     */
    fun fillLine(
        drawScope: DrawScope,
        canvas: Canvas,
        fillPath: Path
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/line/SolidLineDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.line

import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

/**
 * Created by bytebeats on 2021/9/25 : 12:46
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class SolidLineDrawer(
    val thickness: Dp = 3.dp,
    val color: Color = Color.Cyan
) : ILineDrawer {
    private val mPaint by lazy {
        Paint().apply {
            color = this@SolidLineDrawer.color
            style = PaintingStyle.Stroke
            isAntiAlias = true
        }
    }

    override fun drawLine(
        drawScope: DrawScope,
        canvas: Canvas,
        linePath: Path
    ) {
        val lineThickness = with(drawScope) {
            thickness.toPx()
        }
        canvas.drawPath(
            path = linePath,
            paint = mPaint.apply { strokeWidth = lineThickness }
        )
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/line/SolidLineShader.kt
================================================
package me.bytebeats.views.charts.line.render.line

import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/25 : 12:51
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class SolidLineShader(
    val color: Color = Color.Blue
) : ILineShader {
    override fun fillLine(
        drawScope: DrawScope,
        canvas: Canvas,
        fillPath: Path
    ) {
        drawScope.drawPath(
            path = fillPath,
            color = color
        )
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/point/EmptyPointDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.point

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/24 : 20:24
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
object EmptyPointDrawer : IPointDrawer {
    override fun drawPoint(
        drawScope: DrawScope,
        canvas: Canvas,
        center: Offset
    ) {
        //empty point, we do nothing here.
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/point/FilledCircularPointDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.point

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

/**
 * Created by bytebeats on 2021/9/24 : 20:34
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class FilledCircularPointDrawer(
    val diameter: Dp = 8.dp,
    val color: Color = Color.Blue
) : IPointDrawer {

    private val mPaint by lazy {
        Paint().apply {
            color = this@FilledCircularPointDrawer.color
            style = PaintingStyle.Fill
            isAntiAlias = true
        }
    }

    override fun drawPoint(
        drawScope: DrawScope,
        canvas: Canvas,
        center: Offset
    ) {
        with(drawScope as Density) {
            canvas.drawCircle(center, diameter.toPx() / 2F, paint = mPaint)
        }
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/point/HollowCircularPointDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.point

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

/**
 * Created by bytebeats on 2021/9/24 : 20:38
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class HollowCircularPointDrawer(
    val diameter: Dp = 8.dp,
    val lineThickness: Dp = 2.dp,
    val color: Color = Color.Blue
) : IPointDrawer {

    private val mPaint by lazy {
        Paint().apply {
            color = this@HollowCircularPointDrawer.color
            style = PaintingStyle.Stroke
            isAntiAlias = true
        }
    }

    override fun drawPoint(
        drawScope: DrawScope,
        canvas: Canvas,
        center: Offset
    ) {
        with(drawScope as Density) {
            canvas.drawCircle(
                center = center,
                radius = diameter.toPx() / 2F,
                paint = mPaint.apply { strokeWidth = lineThickness.toPx() })
        }
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/point/IPointDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.point

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/24 : 20:22
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface IPointDrawer {

    /**
     * Draw point
     *
     * @param drawScope the scope to draw in
     * @param canvas the canvas to draw on
     * @param center the center point to draw a point
     */
    fun drawPoint(
        drawScope: DrawScope,
        canvas: Canvas,
        center: Offset
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/xaxis/IXAxisDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.xaxis

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/24 : 20:45
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface IXAxisDrawer {

    /**
     * require height
     *
     * @param drawScope the scope to draw in
     */
    fun requireHeight(drawScope: DrawScope): Float

    /**
     * Draw x axis line
     *
     * @param drawScope the scope to draw in
     * @param canvas the canvas to draw on
     * @param drawableArea the area to draw a drawable
     */
    fun drawXAxisLine(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect
    )

    /**
     * Draw labels in x axis
     *
     * @param drawScope the scope to draw in
     * @param canvas the canvas to draw on
     * @param drawableArea the area to draw
     * @param labels the labels to draw on axis
     */
    fun drawXAxisLabels(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect,
        labels: List<*>
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/xaxis/SimpleXAxisDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.xaxis

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import me.bytebeats.views.charts.AxisLabelFormatter
import me.bytebeats.views.charts.toLegacyInt
import me.bytebeats.views.charts.util.FLOAT_1_5

/**
 * Created by bytebeats on 2021/9/24 : 20:50
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
data class SimpleXAxisDrawer(
    val labelTextSize: TextUnit = 12.sp,
    val labelTextColor: Color = Color.Black,
    val drawLabelEvery: Int = 1,// draw label text every $drawLabelEvery, like 1, 2, 3 and so on.
    val axisLineThickness: Dp = 1.dp,
    val axisLineColor: Color = Color.Black,
    val axisLabelFormatter: AxisLabelFormatter = { value -> "$value" }
) : IXAxisDrawer {
    private val mAxisLinePaint by lazy {
        Paint().apply {
            isAntiAlias = true
            color = axisLineColor
            style = PaintingStyle.Stroke
        }
    }

    private val mTextPaint by lazy {
        android.graphics.Paint().apply {
            isAntiAlias = true
            color = labelTextColor.toLegacyInt()
        }
    }

    override fun requireHeight(drawScope: DrawScope): Float = with(drawScope) {
        FLOAT_1_5 * (labelTextSize.toPx() + axisLineThickness.toPx())
    }

    override fun drawXAxisLine(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect
    ) {
        with(drawScope) {
            val lineThickness = axisLineThickness.toPx()
            val y = drawableArea.top + lineThickness / 2F

            canvas.drawLine(
                p1 = Offset(x = drawableArea.left, y = y),
                p2 = Offset(x = drawableArea.right, y = y),
                paint = mAxisLinePaint.apply { strokeWidth = lineThickness })
        }
    }

    override fun drawXAxisLabels(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect,
        labels: List<*>
    ) {
        with(drawScope) {
            val labelPaint = mTextPaint.apply {
                textSize = labelTextSize.toPx()
                textAlign = android.graphics.Paint.Align.CENTER
            }
            val labelIncrements = drawableArea.width / (labels.size - 1)
            labels.forEachIndexed { index, label ->
                if (index.rem(drawLabelEvery) == 0) {
                    val labelValue = axisLabelFormatter(label)
                    val x = drawableArea.left + labelIncrements * index
                    val y = drawableArea.bottom
                    canvas.nativeCanvas.drawText(labelValue, x, y, labelPaint)
                }
            }
        }
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/yaxis/IYAxisDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.yaxis

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope

/**
 * Created by bytebeats on 2021/9/24 : 21:06
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface IYAxisDrawer {

    /**
     * Draw axis line
     *
     * @param drawScope the scope to draw in
     * @param canvas the canvas to draw on
     * @param drawableArea the area to draw a drawable
     */
    fun drawAxisLine(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect
    )

    /**
     * Draw axis labels
     *
     * @param drawScope the scope to draw in
     * @param canvas the canvas to draw on
     * @param drawableArea the area to draw
     * @param minValue the min value of the y axis data
     * @param maxValue the max value of the y axis data
     */
    fun drawAxisLabels(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect,
        minValue: Float,
        maxValue: Float
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/line/render/yaxis/SimpleYAxisDrawer.kt
================================================
package me.bytebeats.views.charts.line.render.yaxis

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import me.bytebeats.views.charts.LabelFormatter
import me.bytebeats.views.charts.toLegacyInt
import kotlin.math.roundToInt

/**
 * Created by bytebeats on 2021/9/24 : 21:08
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

data class SimpleYAxisDrawer(
    val labelTextSize: TextUnit = 12.sp,
    val labelTextColor: Color = Color.Black,
    val drawLabelEvery: Int = 1,
    val labelValueFormatter: LabelFormatter = { value -> "%.1f".format(value) },
    val axisLineThickness: Dp = 1.dp,
    val axisLineColor: Color = Color.Black,
) : IYAxisDrawer {
    private val mAxisLinePaint by lazy {
        Paint().apply {
            isAntiAlias = true
            color = axisLineColor
            style = PaintingStyle.Stroke
        }
    }

    private val mTextPaint by lazy {
        android.graphics.Paint().apply {
            isAntiAlias = true
            color = labelTextColor.toLegacyInt()
        }
    }

    private val mTextBounds = android.graphics.Rect()

    override fun drawAxisLine(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect
    ) = with(drawScope) {
        val lineThickness = axisLineThickness.toPx()
        val x = drawableArea.right - lineThickness / 2F
        canvas.drawLine(
            p1 = Offset(x = x, y = drawableArea.top),
            p2 = Offset(x = x, y = drawableArea.bottom),
            paint = mAxisLinePaint.apply { strokeWidth = lineThickness })
    }

    override fun drawAxisLabels(
        drawScope: DrawScope,
        canvas: Canvas,
        drawableArea: Rect,
        minValue: Float,
        maxValue: Float
    ) {
        with(drawScope) {
            val labelPaint = mTextPaint.apply {
                textSize = labelTextSize.toPx()
                textAlign = android.graphics.Paint.Align.RIGHT
            }

            val minLabelHeight = labelTextSize.toPx() * drawLabelEvery.toFloat()
            val totalHeight = drawableArea.height
            val labelCount = (drawableArea.height / minLabelHeight).roundToInt().coerceAtLeast(2)

            for (i in 0..labelCount) {
                val value = minValue + i * (maxValue - minValue) / labelCount
                val label = labelValueFormatter(value)
                val x = drawableArea.right - axisLineThickness.toPx() - labelTextSize.toPx() / 2F
                labelPaint.getTextBounds(label, 0, label.length, mTextBounds)
                val y =
                    drawableArea.bottom - i * (totalHeight / labelCount) - mTextBounds.height() / 2F
                canvas.nativeCanvas.drawText(label, x, y, labelPaint)
            }
        }
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/pie/PieChart.kt
================================================
package me.bytebeats.views.charts.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.Color
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.tooling.preview.Preview
import me.bytebeats.views.charts.pie.render.ISliceDrawer
import me.bytebeats.views.charts.pie.render.SimpleSliceDrawer
import me.bytebeats.views.charts.simpleChartAnimation

/**
 * Created by bytebeats on 2021/9/24 : 15:34
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

@Composable
fun PieChart(
    pieChartData: PieChartData,
    modifier: Modifier = Modifier,
    animation: AnimationSpec<Float> = simpleChartAnimation(),
    sliceDrawer: ISliceDrawer = SimpleSliceDrawer()
) {
    val transitionProgress = remember(pieChartData.slices) {
        Animatable(initialValue = 0F)
    }

    LaunchedEffect(pieChartData.slices) {
        transitionProgress.animateTo(1F, animationSpec = animation)
    }

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

@Composable
private fun DrawChart(
    pieChartData: PieChartData,
    modifier: Modifier,
    progress: Float,
    sliceDrawer: ISliceDrawer
) {
    val slices = pieChartData.slices

    Canvas(
        modifier = modifier
    ) {
        drawIntoCanvas {
            var startArc = 0F
            slices.forEach { slice ->
                val arc = calculateAngle(
                    sliceLength = slice.value,
                    totalLength = pieChartData.totalLength,
                    progress = progress
                )
                sliceDrawer.drawSlice(
                    drawScope = this,
                    canvas = drawContext.canvas,
                    area = size,
                    startAngle = startArc,
                    sweepAngle = arc,
                    slice = slice
                )
                startArc += arc
            }
        }
    }
}

@Preview
@Composable
private fun PieChartPreview() = PieChart(
    pieChartData = PieChartData(
        slices = listOf(
            PieChartData.Slice(25F, Color.Red),
            PieChartData.Slice(45F, Color.Green),
            PieChartData.Slice(20F, Color.Blue),
        )
    )
)


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/pie/PieChartData.kt
================================================
package me.bytebeats.views.charts.pie

import androidx.compose.ui.graphics.Color

/**
 * Created by bytebeats on 2021/9/24 : 14:32
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

data class PieChartData(val slices: List<Slice>) {
    internal val totalLength: Float
        get() {
            return slices.map { it.value }.sum()
        }

    data class Slice(
        val value: Float,
        val color: Color
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/pie/PieCharts.kt
================================================
package me.bytebeats.views.charts.pie

import me.bytebeats.views.charts.util.FLOAT_360

/**
 * Created by bytebeats on 2021/9/24 : 14:27
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */

internal fun calculateAngle(
    sliceLength: Float,
    totalLength: Float,
    progress: Float
): Float = FLOAT_360 * sliceLength * progress / totalLength


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/pie/render/ISliceDrawer.kt
================================================
package me.bytebeats.views.charts.pie.render

import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.drawscope.DrawScope
import me.bytebeats.views.charts.pie.PieChartData

/**
 * Created by bytebeats on 2021/9/24 : 14:30
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
interface ISliceDrawer {


    /**
     * Draw slice
     * @param drawScope the scope to draw in
     * @param canvas the canvas to draw on
     * @param area the area to draw
     * @param startAngle the start angle to draw a slice
     * @param sweepAngle the sweep angle to draw a slice
     * @param slice the slice data to draw
     */
    fun drawSlice(
        drawScope: DrawScope,
        canvas: Canvas,
        area: Size,
        startAngle: Float,
        sweepAngle: Float,
        slice: PieChartData.Slice
    )
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/pie/render/SimpleSliceDrawer.kt
================================================
package me.bytebeats.views.charts.pie.render

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 me.bytebeats.views.charts.pie.PieChartData
import me.bytebeats.views.charts.util.FLOAT_200

/**
 * Created by bytebeats on 2021/9/24 : 14:36
 * E-mail: happychinapc@gmail.com
 * Quote: Peasant. Educated. Worker
 */
class SimpleSliceDrawer(private val sliceThickness: Float = 25F) : ISliceDrawer {

    init {
        require(sliceThickness in 10F..100F) {
            "Thickness must be between 10 and 100, included"
        }
    }

    private val sectorPaint by lazy {
        Paint().apply {
            isAntiAlias = true
            style = PaintingStyle.Stroke
        }
    }

    private fun computeSectorThickness(area: Size): Float {
        val minSize = area.width.coerceAtMost(area.height)
        return sliceThickness * minSize / FLOAT_200
    }

    private fun computeDrawableArea(area: Size): Rect {
        val sliceThicknessOffset = computeSectorThickness(area) / 2F
        val horizontalOffset = (area.width - area.height) / 2F
        return Rect(
            left = sliceThicknessOffset + horizontalOffset,
            top = sliceThicknessOffset,
            right = area.width - sliceThicknessOffset - horizontalOffset,
            bottom = area.height - sliceThicknessOffset
        )
    }

    override fun drawSlice(
        drawScope: DrawScope,
        canvas: Canvas,
        area: Size,
        startAngle: Float,
        sweepAngle: Float,
        slice: PieChartData.Slice
    ) {
        val sliceThickness = computeSectorThickness(area)
        val drawableArea = computeDrawableArea(area)
        canvas.drawArc(
            rect = drawableArea,
            paint = sectorPaint.apply {
                color = slice.color
                strokeWidth = sliceThickness
            },
            startAngle = startAngle,
            sweepAngle = sweepAngle,
            useCenter = false,
        )
    }
}


================================================
FILE: charts/src/main/java/me/bytebeats/views/charts/util/Floats.kt
================================================
package me.bytebeats.views.charts.util

internal const val FLOAT_10 = 10F
internal const val FLOAT_100 = 100F
internal const val FLOAT_200 = 200F
internal const val FLOAT_255 = 255F
internal const val FLOAT_360 = 360F
internal const val FLOAT_1_5 = 1.5F
internal const val FLOAT_0_5 = .5F


================================================
FILE: config/detekt/detekt.yml
================================================
build:
  maxIssues: 0
  excludeCorrectable: false
  weights:
  # complexity: 2
  # LongParameterList: 1
  # style: 1
  # comments: 1

config:
  validation: true
  warningsAsErrors: false
  checkExhaustiveness: false
  # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
  excludes: ''

processors:
  active: true
  exclude:
    - 'DetektProgressListener'
    - 'KtFileCountProcessor'
    - 'PackageCountProcessor'
    - 'ClassCountProcessor'
    - 'FunctionCountProcessor'
    - 'PropertyCountProcessor'
    - 'ProjectComplexityProcessor'
    - 'ProjectCognitiveComplexityProcessor'
    - 'ProjectLLOCProcessor'
    - 'ProjectCLOCProcessor'
    - 'ProjectLOCProcessor'
    - 'ProjectSLOCProcessor'
    - 'LicenseHeaderLoaderExtension'

console-reports:
  active: true
  exclude:
    - 'ProjectStatisticsReport'
    - 'ComplexityReport'
    - 'NotificationReport'
    - 'FindingsReport'
    - 'FileBasedFindingsReport'
  #  - 'LiteFindingsReport'

output-reports:
  active: true
  exclude:
    # - 'TxtOutputReport'
    # - 'XmlOutputReport'
    # - 'HtmlOutputReport'
    # - 'MdOutputReport'
    - 'SarifOutputReport'

comments:
  active: true
  AbsentOrWrongFileLicense:
    active: false
    licenseTemplateFile: 'license.template'
    licenseTemplateIsRegex: false
  CommentOverPrivateFunction:
    active: false
  CommentOverPrivateProperty:
    active: false
  DeprecatedBlockTag:
    active: false
  EndOfSentenceFormat:
    active: false
    endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
  KDocReferencesNonPublicProperty:
    active: false
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
  OutdatedDocumentation:
    active: false
    matchTypeParameters: true
    matchDeclarationsOrder: true
    allowParamOnConstructorProperties: false
  UndocumentedPublicClass:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
    searchInNestedClass: false
    searchInInnerClass: false
    searchInInnerObject: true
    searchInInnerInterface: true
    searchInProtectedClass: false
  UndocumentedPublicFunction:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
    searchProtectedFunction: false
    ignoreAnnotated:
      - 'Composable'
      - 'Preview'
    ignoreFunction:
      - '[a-z][a-zA-Z0-9]*'
  UndocumentedPublicProperty:
    active: false
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
    searchProtectedProperty: false

complexity:
  active: true
  CognitiveComplexMethod:
    active: false
    threshold: 15
  ComplexCondition:
    active: true
    threshold: 4
  ComplexInterface:
    active: false
    threshold: 10
    includeStaticDeclarations: false
    includePrivateDeclarations: false
    ignoreOverloaded: false
  CyclomaticComplexMethod:
    active: true
    threshold: 15
    ignoreSingleWhenExpression: false
    ignoreSimpleWhenEntries: false
    ignoreNestingFunctions: false
    nestingFunctions:
      - 'also'
      - 'apply'
      - 'forEach'
      - 'isNotNull'
      - 'ifNull'
      - 'let'
      - 'run'
      - 'use'
      - 'with'
  LabeledExpression:
    active: false
    ignoredLabels: [ ]
  LargeClass:
    active: true
    threshold: 600
  LongMethod:
    active: true
    threshold: 100
  LongParameterList:
    active: true
    functionThreshold: 30
    constructorThreshold: 10
    ignoreDefaultParameters: true
    ignoreDataClasses: true
    ignoreAnnotatedParameter: [ ]
  MethodOverloading:
    active: false
    threshold: 6
  NamedArguments:
    active: true
    threshold: 3
    ignoreArgumentsMatchingNames: true
  NestedBlockDepth:
    active: true
    threshold: 4
  NestedScopeFunctions:
    active: true
    threshold: 3
    functions:
      - 'kotlin.apply'
      - 'kotlin.run'
      - 'kotlin.with'
      - 'kotlin.let'
      - 'kotlin.also'
  ReplaceSafeCallChainWithRun:
    active: false
  StringLiteralDuplication:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
    threshold: 3
    ignoreAnnotation: true
    excludeStringsWithLessThan5Characters: true
    ignoreStringsRegex: '$^'
  TooManyFunctions:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/internal/**' ]
    thresholdInFiles: 20
    thresholdInClasses: 30
    thresholdInInterfaces: 20
    thresholdInObjects: 20
    thresholdInEnums: 20
    ignoreDeprecated: false
    ignorePrivate: true
    ignoreOverridden: true
    ignoreAnnotatedFunctions: [ 'Preview' ]

coroutines:
  active: true
  GlobalCoroutineUsage:
    active: false
  InjectDispatcher:
    active: true
    dispatcherNames:
      - 'IO'
      - 'Default'
      - 'Unconfined'
  RedundantSuspendModifier:
    active: true
  SleepInsteadOfDelay:
    active: true
  SuspendFunSwallowedCancellation:
    active: false
  SuspendFunWithCoroutineScopeReceiver:
    active: false
  SuspendFunWithFlowReturnType:
    active: true

empty-blocks:
  active: true
  EmptyCatchBlock:
    active: true
    allowedExceptionNameRegex: '_|(ignore|expected).*'
  EmptyClassBlock:
    active: true
  EmptyDefaultConstructor:
    active: true
  EmptyDoWhileBlock:
    active: true
  EmptyElseBlock:
    active: true
  EmptyFinallyBlock:
    active: true
  EmptyForBlock:
    active: true
  EmptyFunctionBlock:
    active: true
    ignoreOverridden: false
  EmptyIfBlock:
    active: true
  EmptyInitBlock:
    active: true
  EmptyKtFile:
    active: true
  EmptySecondaryConstructor:
    active: true
  EmptyTryBlock:
    active: true
  EmptyWhenBlock:
    active: true
  EmptyWhileBlock:
    active: true

exceptions:
  active: true
  ExceptionRaisedInUnexpectedLocation:
    active: true
    methodNames:
      - 'equals'
      - 'finalize'
      - 'hashCode'
      - 'toString'
  InstanceOfCheckForException:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
  NotImplementedDeclaration:
    active: true
  ObjectExtendsThrowable:
    active: false
  PrintStackTrace:
    active: true
  RethrowCaughtException:
    active: true
  ReturnFromFinally:
    active: true
    ignoreLabeled: false
  SwallowedException:
    active: true
    ignoredExceptionTypes:
      - 'InterruptedException'
      - 'MalformedURLException'
      - 'NumberFormatException'
      - 'ParseException'
    allowedExceptionNameRegex: '_|(ignore|expected).*'
  ThrowingExceptionFromFinally:
    active: true
  ThrowingExceptionInMain:
    active: false
  ThrowingExceptionsWithoutMessageOrCause:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
    exceptions:
      - 'ArrayIndexOutOfBoundsException'
      - 'Exception'
      - 'IllegalArgumentException'
      - 'IllegalMonitorStateException'
      - 'IllegalStateException'
      - 'IndexOutOfBoundsException'
      - 'NullPointerException'
      - 'RuntimeException'
      - 'Throwable'
  ThrowingNewInstanceOfSameException:
    active: true
  TooGenericExceptionCaught:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
    exceptionNames:
      - 'ArrayIndexOutOfBoundsException'
      - 'Error'
      - 'Exception'
      - 'IllegalMonitorStateException'
      - 'IndexOutOfBoundsException'
      - 'NullPointerException'
      - 'RuntimeException'
      - 'Throwable'
    allowedExceptionNameRegex: '_|(ignore|expected).*'
  TooGenericExceptionThrown:
    active: true
    exceptionNames:
      - 'Error'
      - 'Exception'
      - 'RuntimeException'
      - 'Throwable'

naming:
  active: true
  BooleanPropertyNaming:
    active: true
    allowedPattern: '^(is|has|are)'
  ClassNaming:
    active: true
    classPattern: '[A-Z][a-zA-Z0-9]*'
  ConstructorParameterNaming:
    active: true
    parameterPattern: '[a-z][A-Za-z0-9]*'
    privateParameterPattern: '[a-z][A-Za-z0-9]*'
    excludeClassPattern: '$^'
  EnumNaming:
    active: true
    enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
  ForbiddenClassName:
    active: false
    forbiddenName: [ ]
  FunctionMaxLength:
    active: true
    maximumFunctionNameLength: 30
  FunctionMinLength:
    active: true
    minimumFunctionNameLength: 3
  FunctionNaming:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
    functionPattern: '[a-zA-Z][a-zA-Z0-9]*'
    excludeClassPattern: '$^'
    ignoreAnnotated:
      - 'Composable'
  FunctionParameterNaming:
    active: true
    parameterPattern: '[a-z][A-Za-z0-9]*'
    excludeClassPattern: '$^'
  InvalidPackageDeclaration:
    active: true
    rootPackage: ''
    requireRootInDeclaration: false
  LambdaParameterNaming:
    active: true
    parameterPattern: '[a-z][A-Za-z0-9]*|_'
  MatchingDeclarationName:
    active: true
    mustBeFirst: true
  MemberNameEqualsClassName:
    active: true
    ignoreOverridden: true
  NoNameShadowing:
    active: true
  NonBooleanPropertyPrefixedWithIs:
    active: true
  ObjectPropertyNaming:
    active: true
    constantPattern: '[A-Za-z][_A-Za-z0-9]*'
    propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
    privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
  PackageNaming:
    active: true
    packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
  TopLevelPropertyNaming:
    active: true
    constantPattern: '[A-Z][_A-Za-z0-9]*'
    propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
    privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
  VariableMaxLength:
    active: true
    maximumVariableNameLength: 64
  VariableMinLength:
    active: true
    minimumVariableNameLength: 1
  VariableNaming:
    active: true
    variablePattern: '[a-z][A-Za-z0-9]*'
    privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
    excludeClassPattern: '$^'

performance:
  active: true
  ArrayPrimitive:
    active: true
  CouldBeSequence:
    active: false
    threshold: 3
  ForEachOnRange:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
  SpreadOperator:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
  UnnecessaryPartOfBinaryExpression:
    active: true
  UnnecessaryTemporaryInstantiation:
    active: true

potential-bugs:
  active: true
  AvoidReferentialEquality:
    active: true
    forbiddenTypePatterns:
      - 'kotlin.String'
  CastNullableToNonNullableType:
    active: true
  CastToNullableType:
    active: true
  Deprecation:
    active: false
  DontDowncastCollectionTypes:
    active: false
  DoubleMutabilityForCollection:
    active: true
    mutableTypes:
      - 'kotlin.collections.MutableList'
      - 'kotlin.collections.MutableMap'
      - 'kotlin.collections.MutableSet'
      - 'java.util.ArrayList'
      - 'java.util.LinkedHashSet'
      - 'java.util.HashSet'
      - 'java.util.LinkedHashMap'
      - 'java.util.HashMap'
  ElseCaseInsteadOfExhaustiveWhen:
    active: true
    ignoredSubjectTypes: [ ]
  EqualsAlwaysReturnsTrueOrFalse:
    active: true
  EqualsWithHashCodeExist:
    active: true
  ExitOutsideMain:
    active: false
  ExplicitGarbageCollectionCall:
    active: true
  HasPlatformType:
    active: true
  IgnoredReturnValue:
    active: true
    restrictToConfig: true
    returnValueAnnotations:
      - 'CheckResult'
      - '*.CheckResult'
      - 'CheckReturnValue'
      - '*.CheckReturnValue'
    ignoreReturnValueAnnotations:
      - 'CanIgnoreReturnValue'
      - '*.CanIgnoreReturnValue'
    returnValueTypes:
      - 'kotlin.sequences.Sequence'
      - 'kotlinx.coroutines.flow.*Flow'
      - 'java.util.stream.*Stream'
    ignoreFunctionCall: [ ]
  ImplicitDefaultLocale:
    active: true
  ImplicitUnitReturnType:
    active: false
    allowExplicitReturnType: true
  InvalidRange:
    active: true
  IteratorHasNextCallsNextMethod:
    active: true
  IteratorNotThrowingNoSuchElementException:
    active: true
  LateinitUsage:
    active: false
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
    ignoreOnClassesPattern: ''
  MapGetWithNotNullAssertionOperator:
    active: true
  MissingPackageDeclaration:
    active: false
    excludes: [ '**/*.kts' ]
  NullCheckOnMutableProperty:
    active: false
  NullableToStringCall:
    active: false
  PropertyUsedBeforeDeclaration:
    active: false
  UnconditionalJumpStatementInLoop:
    active: false
  UnnecessaryNotNullCheck:
    active: false
  UnnecessaryNotNullOperator:
    active: true
  UnnecessarySafeCall:
    active: true
  UnreachableCatchBlock:
    active: true
  UnreachableCode:
    active: true
  UnsafeCallOnNullableType:
    active: true
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**' ]
  UnsafeCast:
    active: true
  UnusedUnaryOperator:
    active: true
  UselessPostfixExpression:
    active: true
  WrongEqualsTypeParameter:
    active: true

style:
  active: true
  AlsoCouldBeApply:
    active: false
  BracesOnIfStatements:
    active: false
    singleLine: 'never'
    multiLine: 'always'
  BracesOnWhenStatements:
    active: false
    singleLine: 'necessary'
    multiLine: 'consistent'
  CanBeNonNullable:
    active: false
  CascadingCallWrapping:
    active: false
    includeElvis: true
  ClassOrdering:
    active: false
  CollapsibleIfStatements:
    active: false
  DataClassContainsFunctions:
    active: false
    conversionFunctionPrefix:
      - 'to'
    allowOperators: false
  DataClassShouldBeImmutable:
    active: false
  DestructuringDeclarationWithTooManyEntries:
    active: true
    maxDestructuringEntries: 3
  DoubleNegativeLambda:
    active: false
    negativeFunctions:
      - reason: 'Use `takeIf` instead.'
        value: 'takeUnless'
      - reason: 'Use `all` instead.'
        value: 'none'
    negativeFunctionNameParts:
      - 'not'
      - 'non'
  EqualsNullCall:
    active: true
  EqualsOnSignatureLine:
    active: false
  ExplicitCollectionElementAccessMethod:
    active: false
  ExplicitItLambdaParameter:
    active: true
  ExpressionBodySyntax:
    active: false
    includeLineWrapping: false
  ForbiddenAnnotation:
    active: false
    annotations:
      - reason: 'it is a java annotation. Use `Suppress` instead.'
        value: 'java.lang.SuppressWarnings'
      - reason: 'it is a java annotation. Use `kotlin.Deprecated` instead.'
        value: 'java.lang.Deprecated'
      - reason: 'it is a java annotation. Use `kotlin.annotation.MustBeDocumented` instead.'
        value: 'java.lang.annotation.Documented'
      - reason: 'it is a java annotation. Use `kotlin.annotation.Target` instead.'
        value: 'java.lang.annotation.Target'
      - reason: 'it is a java annotation. Use `kotlin.annotation.Retention` instead.'
        value: 'java.lang.annotation.Retention'
      - reason: 'it is a java annotation. Use `kotlin.annotation.Repeatable` instead.'
        value: 'java.lang.annotation.Repeatable'
      - reason: 'Kotlin does not support @Inherited annotation, see https://youtrack.jetbrains.com/issue/KT-22265'
        value: 'java.lang.annotation.Inherited'
  ForbiddenComment:
    active: true
    comments:
      - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.'
        value: 'FIXME:'
      - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.'
        value: 'STOPSHIP:'
      - reason: 'Forbidden TODO todo marker in comment, please do the changes.'
        value: 'TODO:'
    allowedPatterns: ''
  ForbiddenImport:
    active: false
    imports: [ ]
    forbiddenPatterns: ''
  ForbiddenMethodCall:
    active: false
    methods:
      - reason: 'print does not allow you to configure the output stream. Use a logger instead.'
        value: 'kotlin.io.print'
      - reason: 'println does not allow you to configure the output stream. Use a logger instead.'
        value: 'kotlin.io.println'
  ForbiddenSuppress:
    active: false
    rules: [ ]
  ForbiddenVoid:
    active: true
    ignoreOverridden: false
    ignoreUsageInGenerics: false
  FunctionOnlyReturningConstant:
    active: true
    ignoreOverridableFunction: true
    ignoreActualFunction: true
    excludedFunctions: [ ]
  LoopWithTooManyJumpStatements:
    active: true
    maxJumpCount: 1
  MagicNumber:
    active: false
    excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts' ]
    ignoreNumbers:
      - '-1'
      - '0'
      - '1'
      - '2'
    ignoreHashCodeFunction: true
    ignorePropertyDeclaration: true
    ignoreLocalVariableDeclaration: true
    ignoreConstantDeclaration: true
    ignoreCompanionObjectPropertyDeclaration: true
    ignoreAnnotation: true
    ignoreNamedArgument: true
    ignoreEnums: true
    ignoreRanges: true
    ignoreExtensionFunctions: true
    ignoreAnnotated:
      - 'Preview'
  MandatoryBracesLoops:
    active: false
  MaxChainedCallsOnSameLine:
    active: false
    maxChainedCalls: 5
  MaxLineLength:
    active: true
    maxLineLength: 120
    excludePackageStatements: true
    excludeImportStatements: true
    excludeCommentStatements: false
    excludeRawStrings: true
  MayBeConst:
    active: true
  ModifierOrder:
    active: true
  MultilineLambdaItParameter:
    active: false
  MultilineRawStringIndentation:
    active: false
    indentSize: 4
    trimmingMethods:
      - 'trimIndent'
      - 'trimMargin'
  NestedClassesVisibility:
    active: true
  NewLineAtEndOfFile:
    active: true
  NoTabs:
    active: false
  NullableBooleanCheck:
    active: false
  ObjectLiteralToLambda:
    active: true
  OptionalAbstractKeyword:
    active: true
  OptionalUnit:
    active: false
  PreferToOverPairSyntax:
    active: false
  ProtectedMemberInFinalClass:
    active: true
  RedundantExplicitType:
    active: false
  RedundantHigherOrderMapUsage:
    active: true
  RedundantVisibilityModifierRule:
    active: false
  ReturnCount:
    active: true
    max: 2
    excludedFunctions:
      - 'equals'
    excludeLabeled: false
    excludeReturnFromLambda: true
    excludeGuardClauses: false
  SafeCast:
    active: true
  SerialVersionUIDInSerializableClass:
    active: true
  SpacingBetweenPackageAndImports:
    active: false
  StringShouldBeRawString:
    active: false
    maxEscapedCharacterCount: 2
    ignoredCharacters: [ ]
  ThrowsCount:
    active: true
    max: 2
    excludeGuardClauses: false
  TrailingWhitespace:
    active: false
  TrimMultilineRawString:
    active: false
    trimmingMethods:
      - 'trimIndent'
      - 'trimMargin'
  UnderscoresInNumericLiterals:
    active: false
    acceptableLength: 4
    allowNonStandardGrouping: false
  UnnecessaryAbstractClass:
    active: true
  UnnecessaryAnnotationUseSiteTarget:
    active: false
  UnnecessaryApply:
    active: true
  UnnecessaryBackticks:
    active: false
  UnnecessaryBracesAroundTrailingLambda:
    active: false
  UnnecessaryFilter:
    active: true
  UnnecessaryInheritance:
    active: true
  UnnecessaryInnerClass:
    active: false
  UnnecessaryLet:
    active: false
  UnnecessaryParentheses:
    active: false
    allowForUnclearPrecedence: false
  UntilInsteadOfRangeTo:
    active: false
  UnusedImports:
    active: false
  UnusedParameter:
    active: true
    allowedNames: 'ignored|expected'
  UnusedPrivateClass:
    active: true
  UnusedPrivateMember:
    active: true
    allowedNames: ''
    ignoreAnnotated:
      - 'Preview'
  UnusedPrivateProperty:
    active: true
    allowedNames: '_|ignored|expected|serialVersionUID'
  UseAnyOrNoneInsteadOfFind:
    active: true
  UseArrayLiteralsInAnnotations:
    active: true
  UseCheckNotNull:
    active: true
  UseCheckOrError:
    active: true
  UseDataClass:
    active: false
    allowVars: false
  UseEmptyCounterpart:
    active: false
  UseIfEmptyOrIfBlank:
    active: false
  UseIfInsteadOfWhen:
    active: false
    ignoreWhenContainingVariableDeclaration: false
  UseIsNullOrEmpty:
    active: true
  UseLet:
    active: false
  UseOrEmpty:
    active: true
  UseRequire:
    active: true
  UseRequireNotNull:
    active: true
  UseSumOfInsteadOfFlatMapSize:
    active: false
  UselessCallOnNotNull:
    active: true
  UtilityClassWithPublicConstructor:
    active: true
  VarCouldBeVal:
    active: true
    ignoreLateinitVar: false
  WildcardImport:
    active: true
    excludeImports:
      - 'java.util.*'


================================================
FILE: gradle/libs.versions.toml
================================================
[versions]
agp = "8.3.2"
composeCharts = "0.2.1"
kotlin = "1.9.24"
coreKtx = "1.13.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
lifecycleRuntimeKtx = "2.8.1"
activityCompose = "1.9.0"
compose = "1.6.7"
material3 = "1.2.1"
ktCompilerExt = "1.5.14"
dokka = "1.9.20"
detekt = "1.23.6"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
compose-charts = { module = "io.github.bytebeats:compose-charts", version.ref = "composeCharts" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "compose" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics", version.ref = "compose" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "compose" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "compose" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "compose" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
android-library = { id = "com.android.library", version.ref = "agp" }
jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
detekt-gradle-plugin = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }



================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Thu Sep 23 21:39:47 CST 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME


================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
# POM related configuration
GROUP_ID=io.github.bytebeats
# developer related configuration
DEVELOPER_ID=bytebeats
DEVELOPER_NAME=Chen Pan
DEVELOPER_URL=https://github.com/bytebeats
DEVELOPER_EMAIL=happychinapc@gmail.com
# SCM related configuration
SCM_CONNECTION=scm:git:github.com/bytebeats/compose-charts.git
SCM_DEVELOPER_CONNECTION=scm:git:ssh:github.com/bytebeats/compose-charts.git
SCM_URL=https://github.com/bytebeats/compose-charts/tree/master
# License related configuration
LICENSE_NAME=The Apache License, Version 2.0
LICENSE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
LICENCE_DIST=repo
# License related configuration
ISSUE_SYSTEM=GitHub
ISSUE_URL=https://github.com/bytebeats/compose-charts/issues
# organization related configuration
#ORGANIZATION_NAME=
#ORGANIZATION_URL=
# Contributors related configuration
CONTRIBUTOR_NAME=bytebeats
CONTRIBUTOR_EMAIL=happychinapc@gmail.com
CONTRIBUTOR_URL=https://github.com/bytebeats
CONTRIBUTOR_TIMEZONE=China/Beijing
# CI related configuration
CI_SYSTEM=GitHub
CI_URL=https://github.com/bytebeats/compose-charts/actions/
# Library related configuration
PUBLICATION_NAME=ComposeCharts
MODULE_NAME=ComposeCharts
REPO_NAME=SonaTypeMavenCentral
COMPOSE_CHARTS_ARTIFACT_ID=compose-charts
COMPOSE_CHARTS_VERSION=0.2.1
COMPOSE_CHARTS_PACKAGING=aar
COMPOSE_CHARTS_INCEPTION_YEAR=2021
COMPOSE_CHARTS_URL=https://github.com/bytebeats/compose-charts
COMPOSE_CHARTS_DESCRIPTION=compose-charts: Simple Jetpack Compose Charts for multi-platform. Including Android, Web, Desktop.
RELEASES_REPO_URL=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
SNAPSHOTS_REPO_URL=https://s01.oss.sonatype.org/content/repositories/snapshots/


================================================
FILE: gradlew
================================================
#!/usr/bin/env sh

#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=`expr $i + 1`
    done
    case $i in
        0) set -- ;;
        1) set -- "$args0" ;;
        2) set -- "$args0" "$args1" ;;
        3) set -- "$args0" "$args1" "$args2" ;;
        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=`save "$@"`

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem      https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem

@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: settings.gradle.kts
================================================
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
        maven {
            url = uri("https://repo1.maven.org/maven2/")
        }
        mavenLocal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "compose-charts"
include(":app")
include(":charts")
Download .txt
gitextract_1pinupfr/

├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── me/
│           │       └── bytebeats/
│           │           └── views/
│           │               └── charts/
│           │                   └── app/
│           │                       ├── MainActivity.kt
│           │                       └── ui/
│           │                           ├── ComposeCharts.kt
│           │                           ├── Screen.kt
│           │                           ├── ScreenRouter.kt
│           │                           ├── screen/
│           │                           │   ├── HomeScreen.kt
│           │                           │   ├── bar/
│           │                           │   │   ├── BarChartDataModel.kt
│           │                           │   │   └── BarChartScreen.kt
│           │                           │   ├── line/
│           │                           │   │   ├── LineChartDataModel.kt
│           │                           │   │   ├── LineChartScreen.kt
│           │                           │   │   └── PointDrawerType.kt
│           │                           │   └── pie/
│           │                           │       ├── PieChartDataModel.kt
│           │                           │       └── PieChartScreen.kt
│           │                           └── theme/
│           │                               ├── Color.kt
│           │                               ├── Margin.kt
│           │                               ├── Shape.kt
│           │                               ├── Theme.kt
│           │                               └── Type.kt
│           └── res/
│               ├── drawable/
│               │   └── ic_launcher_background.xml
│               ├── drawable-v24/
│               │   └── ic_launcher_foreground.xml
│               ├── mipmap-anydpi-v26/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── themes.xml
├── build.gradle.kts
├── charts/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── consumer-rules.pro
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           └── java/
│               └── me/
│                   └── bytebeats/
│                       └── views/
│                           └── charts/
│                               ├── Animations.kt
│                               ├── AxisLine.kt
│                               ├── Colors.kt
│                               ├── TypeAlias.kt
│                               ├── bar/
│                               │   ├── BarChart.kt
│                               │   ├── BarChartData.kt
│                               │   ├── BarCharts.kt
│                               │   └── render/
│                               │       ├── bar/
│                               │       │   ├── IBarDrawer.kt
│                               │       │   └── SimpleBarDrawer.kt
│                               │       ├── label/
│                               │       │   ├── ILabelDrawer.kt
│                               │       │   └── SimpleLabelDrawer.kt
│                               │       ├── xaxis/
│                               │       │   ├── IXAxisDrawer.kt
│                               │       │   └── SimpleXAxisDrawer.kt
│                               │       └── yaxis/
│                               │           ├── IYAxisDrawer.kt
│                               │           └── SimpleYAxisDrawer.kt
│                               ├── line/
│                               │   ├── LineChart.kt
│                               │   ├── LineChartData.kt
│                               │   ├── LineCharts.kt
│                               │   └── render/
│                               │       ├── line/
│                               │       │   ├── EmptyLineShader.kt
│                               │       │   ├── GradientLineShader.kt
│                               │       │   ├── ILineDrawer.kt
│                               │       │   ├── ILineShader.kt
│                               │       │   ├── SolidLineDrawer.kt
│                               │       │   └── SolidLineShader.kt
│                               │       ├── point/
│                               │       │   ├── EmptyPointDrawer.kt
│                               │       │   ├── FilledCircularPointDrawer.kt
│                               │       │   ├── HollowCircularPointDrawer.kt
│                               │       │   └── IPointDrawer.kt
│                               │       ├── xaxis/
│                               │       │   ├── IXAxisDrawer.kt
│                               │       │   └── SimpleXAxisDrawer.kt
│                               │       └── yaxis/
│                               │           ├── IYAxisDrawer.kt
│                               │           └── SimpleYAxisDrawer.kt
│                               ├── pie/
│                               │   ├── PieChart.kt
│                               │   ├── PieChartData.kt
│                               │   ├── PieCharts.kt
│                               │   └── render/
│                               │       ├── ISliceDrawer.kt
│                               │       └── SimpleSliceDrawer.kt
│                               └── util/
│                                   └── Floats.kt
├── config/
│   └── detekt/
│       └── detekt.yml
├── gradle/
│   ├── libs.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
Condensed preview — 83 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (169K chars).
[
  {
    "path": ".gitignore",
    "chars": 238,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor."
  },
  {
    "path": "LICENSE",
    "chars": 1065,
    "preview": "MIT License\n\nCopyright (c) 2021 Chen Pan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
  },
  {
    "path": "README.md",
    "chars": 6463,
    "preview": "# compose-charts\n\n[![GitHub latest commit](https://badgen.net/github/last-commit/bytebeats/compose-charts)](https://gith"
  },
  {
    "path": "app/.gitignore",
    "chars": 18,
    "preview": "/build\n.idea/\nout/"
  },
  {
    "path": "app/build.gradle.kts",
    "chars": 1657,
    "preview": "plugins {\n    alias(libs.plugins.android.application)\n    alias(libs.plugins.jetbrains.kotlin.android)\n    alias(libs.pl"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 754,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 793,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <appli"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/MainActivity.kt",
    "chars": 442,
    "preview": "package me.bytebeats.views.charts.app\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androi"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/ComposeCharts.kt",
    "chars": 1257,
    "preview": "package me.bytebeats.views.charts.app.ui\n\nimport androidx.compose.animation.Crossfade\nimport androidx.compose.material3."
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/Screen.kt",
    "chars": 225,
    "preview": "package me.bytebeats.views.charts.app.ui\n\n/**\n * Created by bytebeats on 2021/9/30 : 11:34\n * E-mail: happychinapc@gmail"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/ScreenRouter.kt",
    "chars": 610,
    "preview": "package me.bytebeats.views.charts.app.ui\n\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutab"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/screen/HomeScreen.kt",
    "chars": 2330,
    "preview": "package me.bytebeats.views.charts.app.ui.screen\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.c"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/screen/bar/BarChartDataModel.kt",
    "chars": 3214,
    "preview": "package me.bytebeats.views.charts.app.ui.screen.bar\n\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ru"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/screen/bar/BarChartScreen.kt",
    "chars": 6133,
    "preview": "package me.bytebeats.views.charts.app.ui.screen.bar\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androi"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/screen/line/LineChartDataModel.kt",
    "chars": 1803,
    "preview": "package me.bytebeats.views.charts.app.ui.screen.line\n\nimport androidx.compose.runtime.getValue\nimport androidx.compose.r"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/screen/line/LineChartScreen.kt",
    "chars": 4902,
    "preview": "package me.bytebeats.views.charts.app.ui.screen.line\n\nimport androidx.compose.foundation.layout.Arrangement\nimport andro"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/screen/line/PointDrawerType.kt",
    "chars": 242,
    "preview": "package me.bytebeats.views.charts.app.ui.screen.line\n\n/**\n * Created by bytebeats on 2021/9/30 : 17:48\n * E-mail: happyc"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/screen/pie/PieChartDataModel.kt",
    "chars": 2433,
    "preview": "package me.bytebeats.views.charts.app.ui.screen.pie\n\nimport androidx.compose.runtime.getValue\nimport androidx.compose.ru"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/screen/pie/PieChartScreen.kt",
    "chars": 5539,
    "preview": "package me.bytebeats.views.charts.app.ui.screen.pie\n\nimport androidx.compose.foundation.layout.Arrangement\nimport androi"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Color.kt",
    "chars": 225,
    "preview": "package me.bytebeats.views.charts.app.ui.theme\n\nimport androidx.compose.ui.graphics.Color\n\nval Purple200 = Color(0xFFBB8"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Margin.kt",
    "chars": 349,
    "preview": "package me.bytebeats.views.charts.app.ui.theme\n\nimport androidx.compose.ui.unit.dp\n\n/**\n * Created by bytebeats on 2021/"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Shape.kt",
    "chars": 322,
    "preview": "package me.bytebeats.views.charts.app.ui.theme\n\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport andro"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Theme.kt",
    "chars": 1162,
    "preview": "package me.bytebeats.views.charts.app.ui.theme\n\nimport androidx.compose.foundation.isSystemInDarkTheme\nimport androidx.c"
  },
  {
    "path": "app/src/main/java/me/bytebeats/views/charts/app/ui/theme/Type.kt",
    "chars": 812,
    "preview": "package me.bytebeats.views.charts.app.ui.theme\n\nimport androidx.compose.material3.Typography\nimport androidx.compose.ui."
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 5606,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:wi"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "chars": 1702,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    "
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 272,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 272,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 378,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"purple_200\">#FFBB86FC</color>\n    <color name=\"purpl"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 76,
    "preview": "<resources>\n    <string name=\"app_name\">compose-charts</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "chars": 235,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"Theme.Composecharts\" parent=\"android:Theme.Material"
  },
  {
    "path": "build.gradle.kts",
    "chars": 280,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nplugins {\n    alias("
  },
  {
    "path": "charts/.gitignore",
    "chars": 19,
    "preview": "/build/\n.idea/\nout/"
  },
  {
    "path": "charts/build.gradle.kts",
    "chars": 9530,
    "preview": "import com.android.build.gradle.LibraryExtension\nimport org.gradle.jvm.tasks.Jar\nimport org.jetbrains.dokka.gradle.Dokka"
  },
  {
    "path": "charts/consumer-rules.pro",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "charts/proguard-rules.pro",
    "chars": 754,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "charts/src/main/AndroidManifest.xml",
    "chars": 51,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/Animations.kt",
    "chars": 345,
    "preview": "package me.bytebeats.views.charts\n\nimport androidx.compose.animation.core.AnimationSpec\nimport androidx.compose.animatio"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/AxisLine.kt",
    "chars": 798,
    "preview": "package me.bytebeats.views.charts\n\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.Paint\ni"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/Colors.kt",
    "chars": 569,
    "preview": "package me.bytebeats.views.charts\n\nimport androidx.compose.ui.graphics.Color\nimport me.bytebeats.views.charts.util.FLOAT"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/TypeAlias.kt",
    "chars": 266,
    "preview": "package me.bytebeats.views.charts\n\n/**\n * Created by bytebeats on 2021/9/25 : 15:37\n * E-mail: happychinapc@gmail.com\n *"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/BarChart.kt",
    "chars": 4125,
    "preview": "package me.bytebeats.views.charts.bar\n\nimport androidx.compose.animation.core.Animatable\nimport androidx.compose.animati"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/BarChartData.kt",
    "chars": 1285,
    "preview": "package me.bytebeats.views.charts.bar\n\nimport androidx.compose.ui.graphics.Color\nimport me.bytebeats.views.charts.util.F"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/BarCharts.kt",
    "chars": 2374,
    "preview": "package me.bytebeats.views.charts.bar\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.geometry.Size"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/render/bar/IBarDrawer.kt",
    "chars": 718,
    "preview": "package me.bytebeats.views.charts.bar.render.bar\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.gr"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/render/bar/SimpleBarDrawer.kt",
    "chars": 740,
    "preview": "package me.bytebeats.views.charts.bar.render.bar\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.gr"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/render/label/ILabelDrawer.kt",
    "chars": 1155,
    "preview": "package me.bytebeats.views.charts.bar.render.label\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/render/label/SimpleLabelDrawer.kt",
    "chars": 2589,
    "preview": "package me.bytebeats.views.charts.bar.render.label\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/render/xaxis/IXAxisDrawer.kt",
    "chars": 828,
    "preview": "package me.bytebeats.views.charts.bar.render.xaxis\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/render/xaxis/SimpleXAxisDrawer.kt",
    "chars": 1589,
    "preview": "package me.bytebeats.views.charts.bar.render.xaxis\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.u"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/render/yaxis/IYAxisDrawer.kt",
    "chars": 1090,
    "preview": "package me.bytebeats.views.charts.bar.render.yaxis\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/bar/render/yaxis/SimpleYAxisDrawer.kt",
    "chars": 3255,
    "preview": "package me.bytebeats.views.charts.bar.render.yaxis\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.u"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/LineChart.kt",
    "chars": 5392,
    "preview": "package me.bytebeats.views.charts.line\n\nimport androidx.compose.animation.core.Animatable\nimport androidx.compose.animat"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/LineChartData.kt",
    "chars": 1131,
    "preview": "package me.bytebeats.views.charts.line\n\nimport me.bytebeats.views.charts.util.FLOAT_100\n\n/**\n * Created by bytebeats on "
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/LineCharts.kt",
    "chars": 5366,
    "preview": "package me.bytebeats.views.charts.line\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.R"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/line/EmptyLineShader.kt",
    "chars": 502,
    "preview": "package me.bytebeats.views.charts.line.render.line\n\nimport androidx.compose.ui.graphics.Canvas\nimport androidx.compose.u"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/line/GradientLineShader.kt",
    "chars": 755,
    "preview": "package me.bytebeats.views.charts.line.render.line\n\nimport androidx.compose.ui.graphics.Brush\nimport androidx.compose.ui"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/line/ILineDrawer.kt",
    "chars": 617,
    "preview": "package me.bytebeats.views.charts.line.render.line\n\nimport androidx.compose.ui.graphics.Canvas\nimport androidx.compose.u"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/line/ILineShader.kt",
    "chars": 613,
    "preview": "package me.bytebeats.views.charts.line.render.line\n\nimport androidx.compose.ui.graphics.Canvas\nimport androidx.compose.u"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/line/SolidLineDrawer.kt",
    "chars": 1157,
    "preview": "package me.bytebeats.views.charts.line.render.line\n\nimport androidx.compose.ui.graphics.Canvas\nimport androidx.compose.u"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/line/SolidLineShader.kt",
    "chars": 651,
    "preview": "package me.bytebeats.views.charts.line.render.line\n\nimport androidx.compose.ui.graphics.Canvas\nimport androidx.compose.u"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/point/EmptyPointDrawer.kt",
    "chars": 524,
    "preview": "package me.bytebeats.views.charts.line.render.point\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/point/FilledCircularPointDrawer.kt",
    "chars": 1129,
    "preview": "package me.bytebeats.views.charts.line.render.point\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/point/HollowCircularPointDrawer.kt",
    "chars": 1277,
    "preview": "package me.bytebeats.views.charts.line.render.point\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/point/IPointDrawer.kt",
    "chars": 632,
    "preview": "package me.bytebeats.views.charts.line.render.point\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/xaxis/IXAxisDrawer.kt",
    "chars": 1157,
    "preview": "package me.bytebeats.views.charts.line.render.xaxis\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/xaxis/SimpleXAxisDrawer.kt",
    "chars": 3085,
    "preview": "package me.bytebeats.views.charts.line.render.xaxis\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/yaxis/IYAxisDrawer.kt",
    "chars": 1095,
    "preview": "package me.bytebeats.views.charts.line.render.yaxis\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/line/render/yaxis/SimpleYAxisDrawer.kt",
    "chars": 3203,
    "preview": "package me.bytebeats.views.charts.line.render.yaxis\n\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose."
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/pie/PieChart.kt",
    "chars": 2649,
    "preview": "package me.bytebeats.views.charts.pie\n\nimport androidx.compose.animation.core.Animatable\nimport androidx.compose.animati"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/pie/PieChartData.kt",
    "chars": 450,
    "preview": "package me.bytebeats.views.charts.pie\n\nimport androidx.compose.ui.graphics.Color\n\n/**\n * Created by bytebeats on 2021/9/"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/pie/PieCharts.kt",
    "chars": 369,
    "preview": "package me.bytebeats.views.charts.pie\n\nimport me.bytebeats.views.charts.util.FLOAT_360\n\n/**\n * Created by bytebeats on 2"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/pie/render/ISliceDrawer.kt",
    "chars": 891,
    "preview": "package me.bytebeats.views.charts.pie.render\n\nimport androidx.compose.ui.geometry.Size\nimport androidx.compose.ui.graphi"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/pie/render/SimpleSliceDrawer.kt",
    "chars": 2170,
    "preview": "package me.bytebeats.views.charts.pie.render\n\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.geomet"
  },
  {
    "path": "charts/src/main/java/me/bytebeats/views/charts/util/Floats.kt",
    "chars": 289,
    "preview": "package me.bytebeats.views.charts.util\n\ninternal const val FLOAT_10 = 10F\ninternal const val FLOAT_100 = 100F\ninternal c"
  },
  {
    "path": "config/detekt/detekt.yml",
    "chars": 21834,
    "preview": "build:\n  maxIssues: 0\n  excludeCorrectable: false\n  weights:\n  # complexity: 2\n  # LongParameterList: 1\n  # style: 1\n  #"
  },
  {
    "path": "gradle/libs.versions.toml",
    "chars": 2191,
    "preview": "[versions]\nagp = \"8.3.2\"\ncomposeCharts = \"0.2.1\"\nkotlin = \"1.9.24\"\ncoreKtx = \"1.13.1\"\njunit = \"4.13.2\"\njunitVersion = \"1"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 230,
    "preview": "#Thu Sep 23 21:39:47 CST 2021\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributio"
  },
  {
    "path": "gradle.properties",
    "chars": 3141,
    "preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
  },
  {
    "path": "gradlew",
    "chars": 5766,
    "preview": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0"
  },
  {
    "path": "gradlew.bat",
    "chars": 2674,
    "preview": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \""
  },
  {
    "path": "settings.gradle.kts",
    "chars": 661,
    "preview": "pluginManagement {\n    repositories {\n        google {\n            content {\n                includeGroupByRegex(\"com\\\\."
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the bytebeats/compose-charts GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 83 files (151.8 KB), approximately 40.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!