Fix extreme zoom in showing data as missing instead of interpolating the polyline

This commit is contained in:
2026-04-22 00:30:39 -03:00
parent 239ca7c5b4
commit 404b532d38
2 changed files with 26 additions and 8 deletions

View File

@@ -3,13 +3,21 @@ import 'dart:collection';
import '../proto/messages.pb.dart';
import 'packet_buffer.dart';
/// One column of decimated data. `hasData=false` means no packet contributed
/// a value for this channel in the column's time slice.
/// One column of decimated data.
///
/// Tri-state: a column is either
/// - `hasData` — at least one packet in the slice carried this channel
/// (renders as a line/min-max span),
/// - `isGap` — a packet landed in the slice but had no value for this
/// channel (renders as hatched missing-data),
/// - neither — no packet at all fell in the slice (empty, skipped; the
/// polyline interpolates across).
class DecimatedColumn {
const DecimatedColumn(this.min, this.max, this.hasData);
const DecimatedColumn(this.min, this.max, this.hasData, this.isGap);
final double min;
final double max;
final bool hasData;
final bool isGap;
}
/// Classification of a missing-data segment.
@@ -82,14 +90,18 @@ class Decimator {
final mins = List<double>.filled(pixelWidth, double.infinity);
final maxs = List<double>.filled(pixelWidth, double.negativeInfinity);
final has = List<bool>.filled(pixelWidth, false);
final gap = List<bool>.filled(pixelWidth, false);
for (final p in buffer.iterateRange(startUs, endUs)) {
final v = _channelValue(p, channel);
if (v == null) continue;
final tsUs = p.timestampUs.toInt();
var col = ((tsUs - startUs) / pixUs).floor();
if (col < 0) col = 0;
if (col >= pixelWidth) col = pixelWidth - 1;
final v = _channelValue(p, channel);
if (v == null) {
gap[col] = true;
continue;
}
if (!has[col]) {
mins[col] = v;
maxs[col] = v;
@@ -101,7 +113,9 @@ class Decimator {
}
return List.generate(
pixelWidth,
(i) => DecimatedColumn(mins[i], maxs[i], has[i]),
// A column with at least one value renders as data even if some other
// packet in the same slice was missing the field.
(i) => DecimatedColumn(mins[i], maxs[i], has[i], gap[i] && !has[i]),
);
}
@@ -136,7 +150,7 @@ class Decimator {
final hatched = <({int startPx, int endPx})>[];
int? gapStart;
for (var i = 0; i < cols.length; i++) {
if (!cols[i].hasData) {
if (cols[i].isGap) {
gapStart ??= i;
} else if (gapStart != null) {
hatched.add((startPx: gapStart, endPx: i - 1));

View File

@@ -106,10 +106,14 @@ class ChartPainter extends CustomPainter {
bool started = false;
for (var i = 0; i < cols.length; i++) {
final c = cols[i];
if (!c.hasData) {
// Real missing-data gap: break the polyline.
if (c.isGap) {
started = false;
continue;
}
// Empty column (no packet fell here — sub-packet-period zoom). Let the
// path interpolate across.
if (!c.hasData) continue;
final x = xForCol(i);
final yMinPx = yForVal(c.max); // Higher value = smaller y.
final yMaxPx = yForVal(c.min);