Fix extreme zoom in showing data as missing instead of interpolating the polyline
This commit is contained in:
@@ -3,13 +3,21 @@ import 'dart:collection';
|
|||||||
import '../proto/messages.pb.dart';
|
import '../proto/messages.pb.dart';
|
||||||
import 'packet_buffer.dart';
|
import 'packet_buffer.dart';
|
||||||
|
|
||||||
/// One column of decimated data. `hasData=false` means no packet contributed
|
/// One column of decimated data.
|
||||||
/// a value for this channel in the column's time slice.
|
///
|
||||||
|
/// 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 {
|
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 min;
|
||||||
final double max;
|
final double max;
|
||||||
final bool hasData;
|
final bool hasData;
|
||||||
|
final bool isGap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Classification of a missing-data segment.
|
/// Classification of a missing-data segment.
|
||||||
@@ -82,14 +90,18 @@ class Decimator {
|
|||||||
final mins = List<double>.filled(pixelWidth, double.infinity);
|
final mins = List<double>.filled(pixelWidth, double.infinity);
|
||||||
final maxs = List<double>.filled(pixelWidth, double.negativeInfinity);
|
final maxs = List<double>.filled(pixelWidth, double.negativeInfinity);
|
||||||
final has = List<bool>.filled(pixelWidth, false);
|
final has = List<bool>.filled(pixelWidth, false);
|
||||||
|
final gap = List<bool>.filled(pixelWidth, false);
|
||||||
|
|
||||||
for (final p in buffer.iterateRange(startUs, endUs)) {
|
for (final p in buffer.iterateRange(startUs, endUs)) {
|
||||||
final v = _channelValue(p, channel);
|
|
||||||
if (v == null) continue;
|
|
||||||
final tsUs = p.timestampUs.toInt();
|
final tsUs = p.timestampUs.toInt();
|
||||||
var col = ((tsUs - startUs) / pixUs).floor();
|
var col = ((tsUs - startUs) / pixUs).floor();
|
||||||
if (col < 0) col = 0;
|
if (col < 0) col = 0;
|
||||||
if (col >= pixelWidth) col = pixelWidth - 1;
|
if (col >= pixelWidth) col = pixelWidth - 1;
|
||||||
|
final v = _channelValue(p, channel);
|
||||||
|
if (v == null) {
|
||||||
|
gap[col] = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!has[col]) {
|
if (!has[col]) {
|
||||||
mins[col] = v;
|
mins[col] = v;
|
||||||
maxs[col] = v;
|
maxs[col] = v;
|
||||||
@@ -101,7 +113,9 @@ class Decimator {
|
|||||||
}
|
}
|
||||||
return List.generate(
|
return List.generate(
|
||||||
pixelWidth,
|
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})>[];
|
final hatched = <({int startPx, int endPx})>[];
|
||||||
int? gapStart;
|
int? gapStart;
|
||||||
for (var i = 0; i < cols.length; i++) {
|
for (var i = 0; i < cols.length; i++) {
|
||||||
if (!cols[i].hasData) {
|
if (cols[i].isGap) {
|
||||||
gapStart ??= i;
|
gapStart ??= i;
|
||||||
} else if (gapStart != null) {
|
} else if (gapStart != null) {
|
||||||
hatched.add((startPx: gapStart, endPx: i - 1));
|
hatched.add((startPx: gapStart, endPx: i - 1));
|
||||||
|
|||||||
@@ -106,10 +106,14 @@ class ChartPainter extends CustomPainter {
|
|||||||
bool started = false;
|
bool started = false;
|
||||||
for (var i = 0; i < cols.length; i++) {
|
for (var i = 0; i < cols.length; i++) {
|
||||||
final c = cols[i];
|
final c = cols[i];
|
||||||
if (!c.hasData) {
|
// Real missing-data gap: break the polyline.
|
||||||
|
if (c.isGap) {
|
||||||
started = false;
|
started = false;
|
||||||
continue;
|
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 x = xForCol(i);
|
||||||
final yMinPx = yForVal(c.max); // Higher value = smaller y.
|
final yMinPx = yForVal(c.max); // Higher value = smaller y.
|
||||||
final yMaxPx = yForVal(c.min);
|
final yMaxPx = yForVal(c.min);
|
||||||
|
|||||||
Reference in New Issue
Block a user