GraphNG: fix and optimize spanNulls (#29633)

* fix and optimize spanNulls

* AsZero implies spanNulls = true, to prevent null-scanning

* move spanNulls toggle below fillOpacity
This commit is contained in:
Leon Sorokin
2020-12-06 19:26:00 -06:00
committed by GitHub
parent b66bc4a7b1
commit 2d156a385b
6 changed files with 41 additions and 35 deletions

View File

@ -43,7 +43,7 @@ export function mapDimesions(match: XYFieldMatchers, frame: DataFrame, frames?:
export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers): AlignedFrameWithGapTest | null {
const valuesFromFrames: AlignedData[] = [];
const sourceFields: Field[] = [];
let spanNulls = false;
const skipGaps: boolean[][] = [];
// Default to timeseries config
if (!fields) {
@ -55,6 +55,7 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
for (const frame of frames) {
const dims = mapDimesions(fields, frame, frames);
if (!(dims.x.length && dims.y.length)) {
continue; // both x and y matched something!
}
@ -63,9 +64,12 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
throw new Error('Only a single x field is supported');
}
let skipGapsFrame: boolean[] = [];
// Add the first X axis
if (!sourceFields.length) {
sourceFields.push(dims.x[0]);
skipGapsFrame.push(true);
}
const alignedData: AlignedData = [
@ -75,21 +79,23 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
// Add the Y values
for (const field of dims.y) {
let values = field.values.toArray();
let spanNulls = field.config.custom.spanNulls || false;
if (field.config.nullValueMode === NullValueMode.AsZero) {
values = values.map(v => (v === null ? 0 : v));
}
alignedData.push(values);
if (field.config.custom.spanNulls) {
spanNulls = true;
}
alignedData.push(values);
skipGapsFrame.push(spanNulls);
// This will cache an appropriate field name in the field state
getFieldDisplayName(field, frame, frames);
sourceFields.push(field);
}
valuesFromFrames.push(alignedData);
skipGaps.push(skipGapsFrame);
}
if (valuesFromFrames.length === 0) {
@ -97,22 +103,12 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
}
// do the actual alignment (outerJoin on the first arrays)
let { data: alignedData, isGap } = outerJoinValues(valuesFromFrames);
let { data: alignedData, isGap } = outerJoinValues(valuesFromFrames, skipGaps);
if (alignedData!.length !== sourceFields.length) {
throw new Error('outerJoinValues lost a field?');
}
// Wrap the gap function when span nulls exists
if (spanNulls) {
isGap = (u: uPlot, seriesIdx: number, dataIdx: number) => {
if (sourceFields[seriesIdx].config?.custom?.spanNulls) {
return false;
}
return isGap(u, seriesIdx, dataIdx);
};
}
// Replace the values from the outer-join field
return {
frame: {
@ -126,18 +122,20 @@ export function alignDataFrames(frames: DataFrame[], fields?: XYFieldMatchers):
};
}
export function outerJoinValues(tables: AlignedData[]): AlignedDataWithGapTest {
// skipGaps is a tables-matched bool array indicating which series can skip storing indices of original nulls
export function outerJoinValues(tables: AlignedData[], skipGaps?: boolean[][]): AlignedDataWithGapTest {
if (tables.length === 1) {
return {
data: tables[0],
isGap: () => true,
isGap: skipGaps ? (u: uPlot, seriesIdx: number, dataIdx: number) => !skipGaps[0][seriesIdx] : () => true,
};
}
let xVals: Set<number> = new Set();
let xNulls: Array<Set<number>> = [new Set()];
for (const t of tables) {
for (let ti = 0; ti < tables.length; ti++) {
let t = tables[ti];
let xs = t[0];
let len = xs.length;
let nulls: Set<number> = new Set();
@ -147,11 +145,13 @@ export function outerJoinValues(tables: AlignedData[]): AlignedDataWithGapTest {
}
for (let j = 1; j < t.length; j++) {
let ys = t[j];
if (skipGaps == null || !skipGaps[ti][j]) {
let ys = t[j];
for (let i = 0; i < len; i++) {
if (ys[i] == null) {
nulls.add(xs[i]);
for (let i = 0; i < len; i++) {
if (ys[i] == null) {
nulls.add(xs[i]);
}
}
}
}