Remove large series models from reactive data in plots (#6961)

* remove series object from highlights

* remove series models from legend reactive data

* drawing all annotations at once is way faster

* fix multi annotations

* lots of reactive things depending on config

* make annotation lookup faster

* lint

* readd perf test

* address PR comments

* fix highlight typo

---------

Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
Co-authored-by: Shefali Joshi <simplyrender@gmail.com>
This commit is contained in:
Scott Bell
2023-09-06 18:32:36 +02:00
committed by GitHub
parent 0be106f29e
commit 8e917b2679
6 changed files with 265 additions and 180 deletions

View File

@ -78,8 +78,8 @@
:rectangles="rectangles"
:highlights="highlights"
:show-limit-line-labels="limitLineLabels"
:annotated-points="annotatedPoints"
:annotation-selections="annotationSelections"
:annotated-points-by-series="annotatedPointsBySeries"
:annotation-selections-by-series="annotationSelectionsBySeries"
:hidden-y-axis-ids="hiddenYAxisIds"
:annotation-viewing-and-editing-allowed="annotationViewingAndEditingAllowed"
@plotReinitializeCanvas="initCanvas"
@ -245,9 +245,9 @@ export default {
data() {
return {
altPressed: false,
annotatedPointsBySeries: {},
highlights: [],
annotatedPoints: [],
annotationSelections: [],
annotationSelectionsBySeries: {},
annotationsEverLoaded: false,
lockHighlightPoint: false,
yKeyOptions: [],
@ -256,8 +256,6 @@ export default {
plotHistory: [],
selectedXKeyOption: {},
xKeyOptions: [],
seriesModels: [],
legend: {},
pending: 0,
isRealTime: this.openmct.time.isRealTime(),
loaded: false,
@ -346,6 +344,8 @@ export default {
this.abortController = new AbortController();
},
mounted() {
this.seriesModels = [];
this.config = {};
this.yAxisIdVisibility = {};
this.offsetWidth = 0;
@ -357,7 +357,6 @@ export default {
this.setTimeContext = this.setTimeContext.bind(this);
this.config = this.getConfig();
this.legend = this.config.legend;
this.yAxes = [
{
id: this.config.yAxis.id,
@ -455,7 +454,7 @@ export default {
const clickedOption = event.target.closest('.js-autocomplete-options') !== null;
if (!clickedInsidePlot && !clickedInsideInspector && !clickedOption) {
this.rectangles = [];
this.annotationSelections = [];
this.annotationSelectionsBySeries = {};
this.selectPlot();
document.body.removeEventListener('click', this.cancelSelection);
}
@ -639,7 +638,7 @@ export default {
})
);
if (rawAnnotationsForPlot) {
this.annotatedPoints = this.findAnnotationPoints(rawAnnotationsForPlot);
this.annotatedPointsBySeries = this.findAnnotationPoints(rawAnnotationsForPlot);
}
this.annotationsEverLoaded = true;
},
@ -795,7 +794,7 @@ export default {
};
this.config.xAxis.set('range', newRange);
if (!isTick) {
this.annotatedPoints = [];
this.annotatedPointsBySeries = {};
this.clearPanZoomHistory();
this.synchronizeIfBoundsMatch();
this.loadMoreData(newRange, true);
@ -1157,7 +1156,7 @@ export default {
series.closest = series.nearestPoint(point);
return {
series: series,
seriesKeyString: series.keyString,
point: series.closest
};
});
@ -1245,7 +1244,7 @@ export default {
startMarquee(event, annotationEvent) {
this.rectangles = [];
this.annotationSelections = [];
this.annotationSelectionsBySeries = {};
this.canvas.classList.remove('plot-drag');
this.canvas.classList.add('plot-marquee');
@ -1273,7 +1272,10 @@ export default {
const nearbyAnnotations = this.gatherNearbyAnnotations();
if (this.annotationViewingAndEditingAllowed && this.annotationSelections.length) {
if (
this.annotationViewingAndEditingAllowed &&
Object.keys(this.annotationSelectionsBySeries).length
) {
//no annotations were found, but we are adding some now
return;
}
@ -1353,20 +1355,18 @@ export default {
document.body.addEventListener('click', this.cancelSelection);
},
selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBox, event) {
selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBoxBySeries, event) {
let targetDomainObjects = {};
let targetDetails = {};
let annotations = [];
pointsInBox.forEach((pointInBox) => {
if (pointInBox.length) {
const seriesID = pointInBox[0].series.keyString;
const boundingBoxWithId = boundingBoxPerYAxis.find(
(box) => box.id === pointInBox[0].series.get('yAxisId')
);
targetDetails[seriesID] = boundingBoxWithId?.boundingBox;
Object.keys(pointsInBoxBySeries).forEach((seriesKey) => {
const seriesModel = this.getSeries(seriesKey);
const boundingBoxWithId = boundingBoxPerYAxis.find(
(box) => box.id === seriesModel.get('yAxisId')
);
targetDetails[seriesKey] = boundingBoxWithId?.boundingBox;
targetDomainObjects[seriesID] = pointInBox[0].series.domainObject;
}
targetDomainObjects[seriesKey] = seriesModel.domainObject;
});
this.selectPlotAnnotations({
targetDetails,
@ -1375,7 +1375,7 @@ export default {
});
},
findAnnotationPoints(rawAnnotations) {
const annotationsByPoints = [];
const annotationsBySeries = {};
rawAnnotations.forEach((rawAnnotation) => {
if (rawAnnotation.targets) {
const targetValues = Object.values(rawAnnotation.targets);
@ -1390,6 +1390,9 @@ export default {
if (!series) {
return;
}
if (!annotationsBySeries[seriesId]) {
annotationsBySeries[seriesId] = [];
}
boundingBoxPerYAxis.push({
id: series.get('yAxisId'),
@ -1397,15 +1400,23 @@ export default {
});
});
const pointsInBox = this.getPointsInBox(boundingBoxPerYAxis, rawAnnotation);
if (pointsInBox && pointsInBox.length) {
annotationsByPoints.push(pointsInBox.flat());
const pointsInBoxBySeries = this.getPointsInBoxBySeries(
boundingBoxPerYAxis,
rawAnnotation
);
if (pointsInBoxBySeries && Object.values(pointsInBoxBySeries).length) {
Object.keys(pointsInBoxBySeries).forEach((seriesKeyString) => {
const pointsInBox = pointsInBoxBySeries[seriesKeyString];
if (pointsInBox && pointsInBox.length) {
annotationsBySeries[seriesKeyString].push(...pointsInBox);
}
});
}
}
}
});
return annotationsByPoints.flat();
return annotationsBySeries;
},
searchWithFlatbush(seriesData, seriesModel, boundingBox) {
const flatbush = new Flatbush(seriesData.length);
@ -1425,9 +1436,15 @@ export default {
return rangeResults;
},
getPointsInBox(boundingBoxPerYAxis, rawAnnotation) {
getSeries(keyStringToFind) {
const foundSeries = this.seriesModels.find((series) => {
return series.keyString === keyStringToFind;
});
return foundSeries;
},
getPointsInBoxBySeries(boundingBoxPerYAxis, rawAnnotation) {
// load series models in KD-Trees
const seriesKDTrees = [];
const searchResultsBySeries = {};
this.seriesModels.forEach((seriesModel) => {
const boundingBoxWithId = boundingBoxPerYAxis.find(
(box) => box.id === seriesModel.get('yAxisId')
@ -1440,16 +1457,15 @@ export default {
const seriesData = seriesModel.getSeriesData();
if (seriesData && seriesData.length) {
const searchResults = [];
searchResultsBySeries[seriesModel.keyString] = [];
const rangeResults = this.searchWithFlatbush(seriesData, seriesModel, boundingBox);
rangeResults.forEach((id) => {
const seriesDatum = seriesData[id];
if (seriesDatum) {
const result = {
series: seriesModel,
point: seriesDatum
};
searchResults.push(result);
searchResultsBySeries[seriesModel.keyString].push(result);
}
if (rawAnnotation) {
@ -1463,13 +1479,10 @@ export default {
seriesDatum.annotationsById[annotationKeyString] = rawAnnotation;
}
});
if (searchResults.length) {
seriesKDTrees.push(searchResults);
}
}
});
return seriesKDTrees;
return searchResultsBySeries;
},
endAnnotationMarquee(event) {
const boundingBoxPerYAxis = [];
@ -1490,13 +1503,13 @@ export default {
});
});
const pointsInBox = this.getPointsInBox(boundingBoxPerYAxis);
if (!pointsInBox) {
const pointsInBoxBySeries = this.getPointsInBoxBySeries(boundingBoxPerYAxis);
if (!pointsInBoxBySeries || Object.values(pointsInBoxBySeries).length === 0) {
return;
}
this.annotationSelections = pointsInBox.flat();
this.selectNewPlotAnnotations(boundingBoxPerYAxis, pointsInBox, event);
this.annotationSelectionsBySeries = pointsInBoxBySeries;
this.selectNewPlotAnnotations(boundingBoxPerYAxis, this.annotationSelectionsBySeries, event);
},
endZoomMarquee() {
const startPixels = this.marquee.startPixels;