KMPCharts/chart-realtime/README.md
dtrentin 73bbd9ddec feat(chart-realtime): v0.3.0 — dirty flag render, targetFps=30, iOS targets, KDoc, README
- Dirty-flag render: dataVersion counter in RealtimeChartState, skip Canvas draw
  when no new data since last frame (zero GPU work when sensor idle)
- targetFps=30 default in ChartConfig (pass null for max display refresh rate)
- iOS targets: iosX64, iosArm64, iosSimulatorArm64 + Platform.ios.kt actual
- KDoc: RealtimeChart params, dataVersion, ChartConfig.targetFps, LodMode entries,
  AxisLabelMode entries
- README: fixed Maven coords, added LoD/AxisLabels/Material3 sections, updated
  feature list and performance notes
- Tests: RealtimeChartStateTest (17 tests, dataVersion + signal lifecycle)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:11:37 +02:00

107 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
maxBufferSeconds = 3600f, // 1h history per signal
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<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 `targetFps` (default 30) or display refresh if null
- Buffer sized to `maxBufferSeconds × 1000 samples/s` per 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`)
- Dirty-flag skips canvas draw when buffer unchanged since last frame
## Requirements
- Android minSdk 26
- Jetpack Compose / Compose Multiplatform 1.8+
- Kotlin 2.1+