- RealtimeChart composable with Canvas-based rendering - TieredBuffer: 3-tier ring buffer (5min full-rate, 10min @10Hz, 45min @1Hz) for 1h data support with ~5MB footprint - LodDecimator: MEAN / MIN_MAX / MIN_MAX(default) / LTTB render-time decimation - AxisRenderer: X/Y axis with INSIDE/BESIDE/HIDDEN label modes, T0-relative timestamps, scientific notation - SignalRenderer: clipRect-bounded path rendering with LoD - ChartTheme: Material3-compatible dynamic theming (light/dark) - package: dev.dtrentin.chart Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| src | ||
| build.gradle.kts | ||
| README.md | ||
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)
- X axis: scrolling window, seconds from configurable t0
- Y axis: auto-fit or fixed range
- Material3 default theme, fully customizable via
ChartTheme
Installation
// settings.gradle.kts
include(":chart-realtime")
Or Maven local:
./gradlew :chart-realtime:publishToMavenLocal
// build.gradle.kts
implementation("eu.henesis:chart-realtime:0.1.0-SNAPSHOT")
Quick start (3 lines)
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
ChartConfig(
xWindowSeconds = 10f, // visible window width
yRange = YRange.Auto(), // or YRange.Fixed(-1f, 1f)
t0 = T0.FirstSample, // or T0.Fixed(epochMs)
targetFps = null, // null = display refresh rate
maxBufferSeconds = 3600f, // 1h history per signal
theme = ChartTheme( // optional visual override
backgroundColor = Color(0xFF1A1A2E),
gridColor = Color(0x22FFFFFF),
axisColor = Color(0xFFBBBBBB),
),
)
Flow API
// From Flow<Float> (auto-timestamp)
state.collectFrom("accel_x", accelerometerFlow, viewModelScope)
// From Flow<Pair<Long, Float>> (explicit timestamp)
state.collectFrom("ecg", ecgFlow, viewModelScope) // ecgFlow emits (timestampMs, value)
Performance notes
- Data thread pushes at any rate; render thread reads at display refresh (or
targetFps) - Buffer sized to
maxBufferSeconds × 1000 samples/sper signal - 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)
Requirements
- Android minSdk 26
- Jetpack Compose / Compose Multiplatform 1.8+
- Kotlin 2.1+