Some checks are pending
CI / build (push) Waiting to run
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>
181 lines
8.8 KiB
Markdown
181 lines
8.8 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to `chart-realtime` documented here.
|
||
|
||
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||
Project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) 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](https://arxiv.org/abs/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` `Int` → `Long` (fixes 124d overflow @ 200Hz)
|
||
- `kotlinx-datetime` 0.6.2 replaces `expect/actual currentTimeMs` (Platform.kt deleted)
|
||
- `dataVersion` → `mutableLongStateOf` (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.henesis` → `dev.dtrentin`
|
||
- `TieredBuffer` binning: `MEAN` → `MIN_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
|
||
|
||
[Unreleased]: https://github.com/davide-trentin/KMPCharts/compare/v0.5.0...HEAD
|
||
[0.5.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.5.0
|
||
[0.4.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.4.0
|
||
[0.3.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.3.0
|
||
[0.2.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.2.0
|
||
[0.1.0]: https://github.com/davide-trentin/KMPCharts/releases/tag/v0.1.0
|