# chart-realtime KMP real-time line chart for Android. Renders multiple sensor signals at 200Hz+ without frame drops. ## Features - Multiple signals on one chart, added/removed dynamically - FIFO circular buffer — lock-free writes at 200Hz+, safe render reads at 60fps - Level-of-Detail decimation — renders O(pixels) regardless of buffer size (1h+ of data) - Dirty-flag render: skips GPU draw when no new data since last frame - Configurable render rate: default 30 fps, override via `targetFps` (pass `null` for max display Hz) - X axis: scrolling window, seconds from configurable t0 - Y axis: auto-fit or fixed range - Material3 default theme, fully customizable via `ChartTheme` ## Installation ```kotlin // settings.gradle.kts include(":chart-realtime") ``` Or Maven local: ```bash ./gradlew :chart-realtime:publishToMavenLocal ``` ```kotlin // build.gradle.kts implementation("dev.dtrentin:chart-realtime:0.5.0-SNAPSHOT") ``` ## Quick start (3 lines) ```kotlin val state = remember { RealtimeChartState(ChartConfig()) } LaunchedEffect(state) { state.addSignal("ecg", SignalConfig(color = Color.Green)) sensorFlow.collect { v -> state.push("ecg", System.currentTimeMillis(), v) } } RealtimeChart(state = state, modifier = Modifier.fillMaxSize()) ``` ## Configuration ```kotlin ChartConfig( xWindowSeconds = 10f, // visible window width yRange = YRange.Auto(), // or YRange.Fixed(-1f, 1f) t0 = T0.FirstSample, // or T0.Fixed(epochMs) targetFps = 30, // default 30 fps; null = max display refresh rate theme = ChartTheme( // optional visual override backgroundColor = Color(0xFF1A1A2E), gridColor = Color(0x22FFFFFF), axisColor = Color(0xFFBBBBBB), ), ) ``` ## Level of Detail ```kotlin ChartConfig(lodMode = LodMode.MIN_MAX) // default — zero-alloc, preserves peaks ChartConfig(lodMode = LodMode.LTTB) // visually smooth, slight alloc per frame ``` ## Axis Labels ```kotlin ChartConfig( xLabelMode = AxisLabelMode.INSIDE, // default — labels over chart area yLabelMode = AxisLabelMode.BESIDE, // reserved margin for Y labels ) // AxisLabelMode.HIDDEN — hides labels on that axis ``` ## Material3 Theme ```kotlin val theme = rememberMaterialChartTheme() RealtimeChart(state = state, theme = theme) ``` ## Flow API ```kotlin // From Flow (auto-timestamp) state.collectFrom("accel_x", accelerometerFlow, viewModelScope) // From Flow> (explicit timestamp) state.collectFrom("ecg", ecgFlow, viewModelScope) // ecgFlow emits (timestampMs, value) ``` ## Performance notes - Data thread pushes at any rate; render thread reads at `targetFps` (default 30) or display refresh if null - TieredBuffer keeps ~1h history per signal (3-tier ring, no caller config) - LoD decimation reduces >pixel-count data to min/max per pixel column - Pre-allocated arrays — zero GC on hot render path (except LoD scratch, see TODO in `LodDecimator.kt`) - Dirty-flag skips canvas draw when buffer unchanged since last frame ## Requirements - Android minSdk 26 - Jetpack Compose / Compose Multiplatform 1.8+ - Kotlin 2.1+ ## License MIT License — see [LICENSE](../LICENSE).