KMPCharts/CHANGELOG.md
Trentin Davide 07197ed8df
Some checks are pending
CI / build (push) Waiting to run
chore(portfolio): v1.0.0 prep — docs, CI, demos
Autonomous portfolio prep batch. All gates green: 207 tests pass on
iosSimulatorArm64Test, assembleRelease green, apiCheck green, app builds.

Docs:
- CHANGELOG.md (Keep-a-Changelog, retroactive v0.1-v0.5)
- CONTRIBUTING.md (build + style + PR workflow + perf rules)
- chart-realtime/README.md rewrite for v0.5.0 API surface
- docs/PERFORMANCE.md (LoD perf table, alloc budget, memory budget,
  stability report, threading model)

CI (.github/):
- workflows/ci.yml — macos-latest, JDK 21, konan cache, full gate
- workflows/release.yml — tag-triggered, GitHub release with XCFramework
  + AAR; Maven Central as commented TODO (needs OSSRH + GPG secrets)
- dependabot.yml — weekly gradle + actions updates
- pull_request_template.md + 2 issue templates (bug, feature)

Android demo polish:
- DemoScreen + ControlPanel (Material3 surface)
- Sample rate slider (10..200Hz), 1-6 signal count, per-signal waveform
  picker (sine/square/triangle/noise), window seconds, LoD picker, Y
  label mode, Y formatter, interaction toggle, theme switch, status chips
- Single producer coroutine (cleaner crosshair readout, easier rate
  accounting)
- SignalGenerator + WaveformType + DemoConfig + PushCounter

iOS demo scaffold (samples/ios/ChartRealtimeDemo/):
- SPM Package.swift with local binaryTarget pointing to
  chart-realtime/build/XCFrameworks/debug/ChartRealtime.xcframework
- DemoApp.swift + DemoView.swift (smoke test confirming Kotlin types
  accessible from Swift)
- README documents the ComposeUIViewController shim needed in
  chart-realtime/src/iosMain/ for full SwiftUI hosting (deferred to
  v1.0.0 T1)
- xcodebuild against iOS Simulator builds green

Debt:
- gradle.properties: removed kotlin.internal.klibs.non-packed=false
  (Compose-MP 1.11.0 final no longer needs the bypass; apiCheck +
  native compile + 207 tests still green)
- 8 remaining deprecation warnings tied to com.android.library + KMP
  plugin combo; clearing them requires migration to
  com.android.kotlin.multiplatform.library 9.2.0 which forces ABI
  baseline regen + Maven publish restructure + XCFramework hierarchy
  expansion. Deferred as dedicated ticket.

Skipped (need user):
- CODE_OF_CONDUCT.md (content classifier blocks Contributor Covenant
  verbatim)
- Maven Central T1 (Sonatype + GPG)
- Benchmark vs competitors T4 (methodology + Maven coords)
- Logo/banner/GIF T7 (design assets)
- Tag + GitHub release T10 (rename pending)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:54:26 +02:00

8.8 KiB
Raw Blame History

Changelog

All notable changes to chart-realtime documented here.

Format follows Keep a Changelog. Project adheres to Semantic Versioning from 1.0.0 onward.

Pre-1.0.0 releases share the 0.5.0-SNAPSHOT Maven coordinate per group:dev.dtrentin, artifact:chart-realtime. Stable coordinates and Maven Central publishing land in 1.0.0.

Unreleased

Changed

  • KGP 2.3 / AGP 9 deprecation warnings cleanup (in progress)

0.5.0 — 2026-05-22 — Architecture + Interaction

Added

  • Interaction layer (T7) — pinch zoom, drag pan, tap crosshair via ChartInteractionState
    • rememberChartInteractionState(). ViewportMode sealed (Following / Frozen / History(anchorMs)). Swipe-to-right-edge resumes Following. InverseProjection helper: pixel → ms + bisect nearest-sample
  • LodStrategy interface (T2) + three default impls in lod/ package:
    • MinMaxLodStrategy — per-pixel-column min+max preservation
    • LttbLodStrategy — Largest-Triangle-Three-Buckets
    • MinMaxLttbLodStrategy — SOTA per arXiv 2305.00332, 1.80× faster than pure LTTB on Kotlin/Native scalar code (10× target claim assumes Rust + SIMD)
  • SignalRenderer public interface (T3) + LineSignalRenderer default object. Custom renderer impl-able in ~30 LOC. Primitive-only signature, no SignalEntry leak
  • AxisFormatter interface (T4) + four default impls: TimeAxisFormatter, DecimalAxisFormatter, DateTimeAxisFormatter, UnitAxisFormatter
  • ChartConfig split (T5)DataConfig + AxisConfig + RenderConfig + new FrameRate sealed type (Display / Fixed(fps))
  • TieredBuffer.snapshotWindow (T1) — bisect-based windowed snapshot for 30× fewer per-frame copies on small windows in large buffers
  • RealtimeChartState.clear() (T10) — public purge API (preserves signal registrations)
  • RealtimeChartState.signalsArray (T9) — cached array view of signals, invalidated only by addSignal / removeSignal
  • @Immutable / @Stable annotations (T6) on all public types — 0 unstable
  • NumberFormat.formatFixed overflow guard (D4) — routes to scientific @ |v|≥1e19

Changed (BREAKING)

  • Removed LodMode enum — replaced by LodStrategy interface
  • Removed LodDecimator internal class — replaced by LodStrategy impls
  • Removed ChartConfig.targetFps field — replaced by RenderConfig.frameRate: FrameRate
  • ChartConfig flat fields removed — accessed via nested configs: config.data.xWindowSeconds, config.render.theme, config.axis.xLabelMode, etc. Top-level inline accessors retained for xWindowSeconds and theme
  • SignalRenderer signature changed — internal object → public interface with primitive-only params (Color, Float, FloatArray, Path)
  • SignalConfig.renderer field added — per-signal renderer override, defaults to LineSignalRenderer singleton
  • LTTB upper bound aligned to half-open semantic (D3)[start, start+windowMs) matches TieredBuffer.snapshot. Samples at exact upper bound now excluded
  • Pair<Float, Float> removed from AxisRenderer.resolveYRange — replaced by FloatArray out param

Performance

  • LineSignalRenderer caches Stroke instances per strokeWidth (T8)
  • AxisRenderer caches TextStyle instances (16-entry cap)
  • Render path: 0 Pair/Map/Iterator allocations per frame at steady state

Toolchain

  • Kotlin 2.1.0 → 2.3.21
  • Compose Multiplatform 1.8.0 → 1.11.0
  • Android Gradle Plugin 8.7.3 → 9.2.0
  • Gradle 8.11.1 → 9.5.1
  • coroutines 1.9.0 → 1.11.0; kotlin-test → 2.3.21
  • Dropped kotlinx-datetime — replaced by stdlib kotlin.time.Clock (Kotlin 2.3 stable)
  • Dropped iosX64 target — Compose Multiplatform 1.11.0 no longer ships ios_x64 variants (Intel Mac iOS simulators deprecated by Apple since Xcode 15+)

Tests

  • 107 → 207 (+100). Coverage: lod/ (24), interaction/ (29), model/ChartConfigTest (8), render/AxisFormatterTest (10), RealtimeChartStateTest (+9), NumberFormatTest (+6), TieredBufferTest (+10), CircularBufferTest (+10), AxisRendererTest (3), LineSignalRendererTest (2)

API surface

  • chart-realtime.api: 161 → 428 LOC
  • chart-realtime.klib.api: 211 → 501 LOC

0.4.0 — 2026-05-21 — Portfolio Hardening

15 tasks shipped via 3-reviewer audit (android-reviewer + mobile-performance-reviewer + mobile-architect). Library moved from "prototype" to "senior-engineer-built KMP library".

Added

  • LICENSE MIT
  • explicitApi() Strict + Binary Compatibility Validator (BCV) 0.16.3 + klib enabled
  • ABI baseline locked (chart-realtime.api 161 LOC, chart-realtime.klib.api 211 LOC)
  • hasData boolean flag (replaces POSITIVE_INFINITY / Float.MAX_VALUE sentinels)
  • Snapshot.withMutableSnapshot + SnapshotApplyConflictException retry helper
  • M4 binning for Tier1/Tier2 (firstTs/V, minTs/V, maxTs/V, lastTs/V) with chronological dedup
  • NaN/Inf/backward-timestamp guards on push; per-signal lastPushedTs tracking
  • NumberFormat extracted from AxisRenderer (38 tests)
  • 25 buffer/LoD tests (tier roll, window crossing, clear, LTTB branches)

Changed

  • CircularBuffer.writeIndex IntLong (fixes 124d overflow @ 200Hz)
  • kotlinx-datetime 0.6.2 replaces expect/actual currentTimeMs (Platform.kt deleted)
  • dataVersionmutableLongStateOf (Compose-observable, idle = 0 recompositions)
  • Single buffer.snapshot() per signal per frame via per-signal scratch arrays
  • TIER1_CAPACITY 12k → 24k, TIER2_CAPACITY 5.4k → 10.8k (×2 → ×4 for M4 binning)
  • Memory per signal: ~760 KB → ~1.14 MB

Removed

  • RenderLoop.kt, rememberFrameTick (deleted)
  • maxBufferSeconds, xLabelDecimals, SignalConfig.label (dead config)
  • All !! and @Suppress("UNUSED_EXPRESSION") from commonMain

Internal lockdown

  • CircularBuffer, TieredBuffer, LodDecimator, dataVersion, rememberFrameTick, capacity constants

Tests

  • 33 → 107 (+74)

0.3.0 — 2026-05-21 — iOS Build Pipeline + Dirty Flag Render

Added

  • iOS targets compile + link green: iosX64, iosArm64, iosSimulatorArm64
  • applyDefaultHierarchyTemplate() auto-wires iosMain / iosTest
  • XCFramework ChartRealtime (debug + release): ios-arm64 + ios-arm64_x86_64-simulator
  • Dirty-flag render: dataVersion counter, skip Canvas draw when no new data
  • targetFps = 30 default in ChartConfig (null = max display refresh rate)
  • iOS-side Platform.ios.kt: NSDate() + timeIntervalSinceReferenceDate + 1970 epoch offset (978_307_200s)
  • KDoc on RealtimeChart, RealtimeChartState, ChartConfig, LodMode, AxisLabelMode
  • README sections: LoD, AxisLabels, Material3

Fixed

  • Multiplatform replacements for JVM-only String.format: formatFixed / formatScientific / pow10 helpers in AxisRenderer
  • kotlin.concurrent.Volatile + kotlin.jvm.JvmName imports (OptionalExpectation)
  • X axis: show negative values instead of clamping to "0s"

Tests

  • 17 → 33 (RealtimeChartStateTest: dataVersion + signal lifecycle)

0.2.0 — 2026-05-21 — Polish

Added

  • TieredBuffer: 3-tier ring buffer (5min full-rate, 10min @ 10Hz, 45min @ 1Hz), ~5MB footprint for 1h of data
  • LodMode.LTTB (Largest-Triangle-Three-Buckets, O(n) decimation)
  • AxisLabelMode (INSIDE / BESIDE / HIDDEN) with chartLeft / chartBottom insets
  • T0-relative X-axis timestamps with mm:ss / hh:mm:ss formatting (negative supported)
  • Material3 dynamic theming (light/dark, wallpaper-based, API 31+)
  • Camera cutout / edge-to-edge support
  • Sensor domain layer (Clean Architecture)
  • Koin 4.0.0 DI wiring
  • Sensor rate cap: 200Hz (5_000μs)

Changed

  • Package rename: eu.henesisdev.dtrentin
  • TieredBuffer binning: MEANMIN_MAX (composable across tier boundaries, no synthetic values, no C7 spike artifacts)
  • LodDecimator: pre-allocated scratch arrays (zero GC at render time)

Removed

  • LodMode.MEAN (introduced synthetic values absent in raw signal)

0.1.0 — 2026-05-20 — MVP

Initial implementation. Six phases shipped:

  1. Module scaffold (KMP + Compose Multiplatform + Android library)
  2. CircularBuffer + LodDecimator
  3. RealtimeChartState API
  4. RealtimeChart composable + Canvas-based SignalRenderer + AxisRenderer
  5. Demo app (:app)
  6. Polish + initial docs