KMPCharts/.paul/phases/v0.2.0-plan-SUMMARY.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

8.2 KiB
Raw Blame History

phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established duration started completed
v0.2.0-polish v0.2.0 chart-realtime
kmp
compose
canvas
buffer
lod
tiered-buffer
lttb
min-max
axis
material3
phase provides
v0.1.0-mvp CircularBuffer, LodDecimator, RealtimeChart composable, basic AxisRenderer
TieredBuffer (3-tier 1h ring buffer, MIN_MAX binning, zero-alloc)
LodMode enum (MIN_MAX, LTTB)
LTTB render-time decimation
AxisLabelMode (INSIDE/BESIDE/HIDDEN) with chartLeft/chartBottom insets
T0-relative X-axis timestamps with mm:ss/hh:mm:ss formatting
Material3 dynamic theming (light/dark, wallpaper-based)
Camera cutout / edge-to-edge support
Sensor domain layer (Clean Architecture)
Koin DI wiring
Package
dev.dtrentin.chart
Git repository on ssh://3nt-git.duckdns.org:222/davide.trentin/KMPCharts.git
future iOS target
future persistence layer
added patterns
koin-4.0.0
kotlinx-coroutines-android
androidx-lifecycle-runtime-compose
TieredBuffer hot-path write
MIN_MAX composable decimation
LodDecimator as class with pre-alloc scratch
created modified
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/buffer/TieredBuffer.kt
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/model/LodMode.kt
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/model/AxisLabelMode.kt
sensor-domain/src/commonMain/kotlin/dev/dtrentin/sensor/domain/
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/buffer/LodDecimator.kt
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/buffer/CircularBuffer.kt
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/render/AxisRenderer.kt
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/render/SignalRenderer.kt
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/RealtimeChart.kt
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/RealtimeChartState.kt
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/model/ChartConfig.kt
chart-realtime/src/commonMain/kotlin/dev/dtrentin/chart/model/ChartTheme.kt
TieredBuffer over flat ring: O(signals × constant) memory, scales to 20+ signals
MIN_MAX binning for tiers: composable operation, zero visual discontinuity at boundaries
MEAN removed: introduces values never in raw signal, disinformation for sensor data
LodDecimator as class: pre-alloc scratch arrays, zero GC at render time
Sensor rate capped at 200Hz: thermal/battery optimization
Package dev.dtrentin: personal project, not company
MIN_MAX composability: MIN(bin_mins)=global_MIN across tier boundaries
TieredBuffer snapshot always time-sorted: tier2→tier1→tier0 with tier0Start=max(windowStart,tier0Boundary)
Render arrays pre-alloc in RealtimeChart via remember { }, zero alloc per frame
~8h (multi-session) 2026-05-20T00:00:00Z 2026-05-21T00:00:00Z

v0.2.0 Plan Summary

chart-realtime KMP library rebuilt from scratch: TieredBuffer (1h/20-signal), LTTB+MIN_MAX LoD, axis labels with INSIDE/BESIDE/HIDDEN modes, Material3 dynamic theming, Clean Architecture sensor domain — shipped as v0.5.0-SNAPSHOT.

Performance

Metric Value
Duration ~8h multi-session
Started 2026-05-20
Completed 2026-05-21
Versions shipped 0.2.0→0.3.0→0.4.0→0.4.1→0.5.0-SNAPSHOT
Git commits 3 (initial + 2 incremental)
Files modified 20+

Acceptance Criteria Results

Criterion Status Notes
Axis text labels visible Pass AxisRenderer TextMeasurer, INSIDE/BESIDE/HIDDEN modes
Dynamic X-window from UI Pass Slider 23600s, StateFlow, MAX_BUFFER_SECONDS=3600
Material3 theme compliance Pass rememberMaterialChartTheme(), dynamic color API 31+
User-decided signal colors Pass SignalConfig.color, ChartTheme separate
Camera cutout / edge-to-edge Pass enableEdgeToEdge() + Scaffold innerPadding
1h data support without OOM Pass TieredBuffer ~18MB for 6 signals
No tier boundary visual artifact Pass MIN_MAX composable + tier0 lower-bound fix
LodDecimator zero-alloc Pass Class with pre-alloc scratch arrays
Negative X-axis values Pass formatTimeSec handles negative elapsed time
Sensor rate cap 200Hz Pass SensorRepositoryImpl 5_000μs
Package renamed dev.dtrentin Pass eu.henesis → dev.dtrentin everywhere
Remote git repository Pass ssh://3nt-git.duckdns.org:222/davide.trentin/KMPCharts.git

Accomplishments

  • TieredBuffer: 3-tier ring buffer (tier0=5min@200Hz, tier1=10min@10Hz MIN_MAX, tier2=45min@1Hz MIN_MAX). ~18MB for 6 signals vs 259MB naive flat at 1000Hz assumption
  • MIN_MAX composability: tier boundary artifacts eliminated. MIN(tier_mins)=global_MIN mathematically proven. MEAN removed as inherently dishonest for raw sensor data
  • LTTB algorithm: O(n) Largest-Triangle-Three-Buckets in LodDecimator. Preserves peaks, smooth trend line, better than mean without signal distortion
  • Axis overhaul: INSIDE/BESIDE/HIDDEN modes, chartLeft/chartBottom insets, T0-relative timestamps (mm:ss/hh:mm:ss), scientific notation for |y|<0.01 or >1e5, negative values, grid toggle

Deviations from v0.2.0 Plan

Type Count Description
Scope additions 15+ All features beyond Material3/axis/xWindow/scratch
Auto-fixed bugs 8 Tier boundary, LTTB samplePtr, X label sticky, Y label clipping, LoD path unravel, etc.
Deferred 1 iOS expect/actual — still commonMain only

Key deviations:

  • Phase 4 (scratch optimization): addressed differently — LodDecimator converted from object to class with pre-allocated instance fields, not param-passing as planned
  • CircularBuffer replaced by TieredBuffer at storage level: larger scope than originally planned
  • MEAN LodMode added then removed in same milestone after analysis showed it was harmful

Key Decisions

Decision Rationale Impact
TieredBuffer over flat ring O(N×constant) memory, scales to 20+ signals at any rate Core buffer architecture
MIN_MAX tier binning Composable: MIN(mins)=global_MIN, no artifacts Eliminates tier discontinuity
MEAN removed from LodMode Introduces synthetic values never in raw signal Cleaner API, honest visualization
Sensor cap 200Hz not FASTEST Thermal/battery optimization, 200Hz exceeds visual need Reduces CPU interrupts 4-10×
dev.dtrentin namespace Personal project, not company IP Package identity

Bugs Fixed

Bug Root Cause Fix
TieredBuffer tier artifact tier0 snapshot lacked ts >= tier0BoundaryMs filter → double-inclusion → col-sort broken Added tier0Start = maxOf(windowStart, tier0BoundaryMs)
LoD path unravel LodDecimator mapped out-of-window samples (tRelMs<0) to x=0 Added in-window filter before binning
X label sticky at axis coerceAtLeast(chartLeft) clamped all early labels to axis line Replaced with if (labelX >= chartLeft) skip
Y labels clipped bottom Used raw 14f px instead of density-aware sp with(this) { 12.sp.toPx() }
Signal line outside chart No clipRect on drawPath Added clipRect(left=chartLeft…)
Negative X shows "0s" coerceAtLeast(0L) abs() + - prefix for negative
OOM at MAX_X_WINDOW=3600 MAX_SAMPLE_RATE=1000Hz assumed, 259MB pre-alloc Reduced to 200Hz, TieredBuffer replaces flat ring

Next Phase Readiness

Ready:

  • Library stable at v0.5.0-SNAPSHOT, published to mavenLocal
  • Remote git: ssh://3nt-git.duckdns.org:222/davide.trentin/KMPCharts.git
  • AndroidChartsApp consuming library, builds clean
  • Clean Architecture sensor domain in place

Potential next work:

  • Dirty flag render (skip frame if no new data) — thermal/battery improvement
  • targetFps=30 default — thermal improvement
  • iOS expect/actual for currentTimeMs (currently Android only)
  • Publish to Maven Central or GitHub Packages
  • KDoc on public API surface
  • README with integration example

Deferred:

  • iOS support (expect/actual beyond currentTimeMs)
  • Adaptive sensor rate based on xWindowSeconds

Phase: v0.2.0-polish, completed 2026-05-21