TimeSeries: Fix rendering of sparse points with null gaps at outer edges (#106507)

This commit is contained in:
Leon Sorokin
2025-06-11 15:43:54 -05:00
committed by GitHub
parent 7864c1660f
commit 44e7be134d
+43 -32
View File
@@ -347,48 +347,59 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn = ({
pointsFilter = (u, seriesIdx, show, gaps) => {
let filtered = [];
let series = u.series[seriesIdx];
if (!show && gaps && gaps.length) {
const [firstIdx, lastIdx] = series.idxs!;
const xData = u.data[0];
if (!show) {
const yData = u.data[seriesIdx];
const firstPos = Math.round(u.valToPos(xData[firstIdx], 'x', true));
const lastPos = Math.round(u.valToPos(xData[lastIdx], 'x', true));
if (gaps[0][0] === firstPos) {
filtered.push(firstIdx);
}
if (gaps && gaps.length) {
const firstIdx = u.posToIdx(gaps[0][0], true);
// show single points between consecutive gaps that share end/start
for (let i = 0; i < gaps.length; i++) {
let thisGap = gaps[i];
let nextGap = gaps[i + 1];
if (yData[firstIdx - 1] == null) {
filtered.push(firstIdx);
}
if (nextGap && thisGap[1] === nextGap[0]) {
// approx when data density is > 1pt/px, since gap start/end pixels are rounded
let approxIdx = u.posToIdx(thisGap[1], true);
// show single points between consecutive gaps that share end/start
for (let i = 0; i < gaps.length; i++) {
let thisGap = gaps[i];
let nextGap = gaps[i + 1];
if (yData[approxIdx] == null) {
// scan left/right alternating to find closest index with non-null value
for (let j = 1; j < 100; j++) {
if (yData[approxIdx + j] != null) {
approxIdx += j;
break;
}
if (yData[approxIdx - j] != null) {
approxIdx -= j;
break;
if (nextGap && thisGap[1] === nextGap[0]) {
// approx when data density is > 1pt/px, since gap start/end pixels are rounded
let approxIdx = u.posToIdx(thisGap[1], true);
if (yData[approxIdx] == null) {
// scan left/right alternating to find closest index with non-null value
for (let j = 1; j < 100; j++) {
if (yData[approxIdx + j] != null) {
approxIdx += j;
break;
}
if (yData[approxIdx - j] != null) {
approxIdx -= j;
break;
}
}
}
}
filtered.push(approxIdx);
filtered.push(approxIdx);
}
}
const lastIdx = u.posToIdx(gaps[gaps.length - 1][1], true);
if (yData[lastIdx + 1] == null) {
filtered.push(lastIdx);
}
}
if (gaps[gaps.length - 1][1] === lastPos) {
filtered.push(lastIdx);
// single point surrounded by nulls
else {
// meh heuristic
if (yData[0] == null && yData[yData.length - 1] == null) {
for (let i = 0; i < yData.length; i++) {
if (yData[i] != null) {
filtered.push(i);
}
}
}
}
}