openmct/src/plugins/plot/chart/MctChart.vue
Shefali Joshi 01f724959d
Ensure limit lines for both the old and new y axes are redrawn when a series moves from one y axis to another (#6181)
Optimize initialization of Plot configuration
Ensure the the y axis form correctly saves any changes to the configuration
Fix excluded limits test
2023-01-26 17:11:13 +00:00

881 lines
32 KiB
Vue

<!--
Open MCT, Copyright (c) 2014-2022, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<!-- eslint-disable vue/no-v-html -->
<template>
<div class="gl-plot-chart-area">
<span v-html="canvasTemplate"></span>
<span v-html="canvasTemplate"></span>
<div
ref="limitArea"
class="js-limit-area"
></div>
</div>
</template>
<script>
import eventHelpers from "../lib/eventHelpers";
import { DrawLoader } from '../draw/DrawLoader';
import MCTChartLineLinear from './MCTChartLineLinear';
import MCTChartLineStepAfter from './MCTChartLineStepAfter';
import MCTChartPointSet from './MCTChartPointSet';
import MCTChartAlarmPointSet from './MCTChartAlarmPointSet';
import MCTChartAlarmLineSet from "./MCTChartAlarmLineSet";
import configStore from "../configuration/ConfigStore";
import PlotConfigurationModel from "../configuration/PlotConfigurationModel";
import LimitLine from "./LimitLine.vue";
import LimitLabel from "./LimitLabel.vue";
import Vue from 'vue';
const MARKER_SIZE = 6.0;
const HIGHLIGHT_SIZE = MARKER_SIZE * 2.0;
const ANNOTATION_SIZE = MARKER_SIZE * 3.0;
const CLEARANCE = 15;
export default {
inject: ['openmct', 'domainObject', 'path'],
props: {
rectangles: {
type: Array,
default() {
return [];
}
},
highlights: {
type: Array,
default() {
return [];
}
},
annotatedPoints: {
type: Array,
default() {
return [];
}
},
annotationSelections: {
type: Array,
default() {
return [];
}
},
showLimitLineLabels: {
type: Object,
default() {
return {};
}
},
hiddenYAxisIds: {
type: Array,
default() {
return [];
}
},
annotationViewingAndEditingAllowed: {
type: Boolean,
required: true
}
},
data() {
return {
canvasTemplate: '<canvas style="position: absolute; background: none; width: 100%; height: 100%;"></canvas>'
};
},
watch: {
highlights() {
this.scheduleDraw();
},
annotatedPoints() {
this.scheduleDraw();
},
annotationSelections() {
this.scheduleDraw();
},
rectangles() {
this.scheduleDraw();
},
showLimitLineLabels() {
this.drawLimitLines();
},
hiddenYAxisIds() {
this.hiddenYAxisIds.forEach(id => {
this.resetYOffsetAndSeriesDataForYAxis(id);
this.drawLimitLines();
});
this.scheduleDraw();
}
},
mounted() {
eventHelpers.extend(this);
this.config = this.getConfig();
this.isDestroyed = false;
this.lines = [];
this.limitLines = [];
this.pointSets = [];
this.alarmSets = [];
const yAxisId = this.config.yAxis.get('id');
this.offset = {
[yAxisId]: {}
};
this.listenTo(this.config.yAxis, 'change:key', this.resetYOffsetAndSeriesDataForYAxis.bind(this, yAxisId), this);
this.listenTo(this.config.yAxis, 'change', this.updateLimitsAndDraw);
if (this.config.additionalYAxes.length) {
this.config.additionalYAxes.forEach(yAxis => {
const id = yAxis.get('id');
this.offset[id] = {};
this.listenTo(yAxis, 'change', this.updateLimitsAndDraw);
this.listenTo(yAxis, 'change:key', this.resetYOffsetAndSeriesDataForYAxis.bind(this, id), this);
});
}
this.seriesElements = new WeakMap();
this.seriesLimits = new WeakMap();
let canvasEls = this.$parent.$refs.chartContainer.querySelectorAll("canvas");
const mainCanvas = canvasEls[1];
const overlayCanvas = canvasEls[0];
if (this.initializeCanvas(mainCanvas, overlayCanvas)) {
this.draw();
}
this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
this.listenTo(this.config.xAxis, 'change', this.updateLimitsAndDraw);
this.config.series.forEach(this.onSeriesAdd, this);
this.$emit('chartLoaded');
},
beforeDestroy() {
this.destroy();
},
methods: {
getConfig() {
const configId = this.openmct.objects.makeKeyString(this.domainObject.identifier);
let config = configStore.get(configId);
if (!config) {
config = new PlotConfigurationModel({
id: configId,
domainObject: this.domainObject,
openmct: this.openmct
});
configStore.add(configId, config);
}
return config;
},
reDraw(mode, o, series) {
this.changeInterpolate(mode, o, series);
this.changeMarkers(mode, o, series);
this.changeAlarmMarkers(mode, o, series);
this.changeLimitLines(mode, o, series);
},
onSeriesAdd(series) {
this.listenTo(series, 'change:xKey', this.reDraw, this);
this.listenTo(series, 'change:interpolate', this.changeInterpolate, this);
this.listenTo(series, 'change:markers', this.changeMarkers, this);
this.listenTo(series, 'change:alarmMarkers', this.changeAlarmMarkers, this);
this.listenTo(series, 'change:limitLines', this.changeLimitLines, this);
this.listenTo(series, 'change:yAxisId', this.resetAxisAndRedraw, this);
// TODO: Which other changes is the listener below reacting to?
this.listenTo(series, 'change', this.scheduleDraw);
this.listenTo(series, 'add', this.onAddPoint);
this.makeChartElement(series);
this.makeLimitLines(series);
},
onAddPoint(point, insertIndex, series) {
const mainYAxisId = this.config.yAxis.get('id');
const seriesYAxisId = series.get('yAxisId');
const xRange = this.config.xAxis.get('displayRange');
let yRange;
if (seriesYAxisId === mainYAxisId) {
yRange = this.config.yAxis.get('displayRange');
} else {
yRange = this.config.additionalYAxes.find(
yAxis => yAxis.get('id') === seriesYAxisId
).get('displayRange');
}
const xValue = series.getXVal(point);
const yValue = series.getYVal(point);
// if user is not looking at data within the current bounds, don't draw the point
if ((xValue > xRange.min) && (xValue < xRange.max)
&& (yValue > yRange.min) && (yValue < yRange.max)) {
this.scheduleDraw();
}
},
changeInterpolate(mode, o, series) {
if (mode === o) {
return;
}
const elements = this.seriesElements.get(series);
elements.lines.forEach(function (line) {
this.lines.splice(this.lines.indexOf(line), 1);
line.destroy();
}, this);
elements.lines = [];
const newLine = this.lineForSeries(series);
if (newLine) {
elements.lines.push(newLine);
this.lines.push(newLine);
}
},
changeAlarmMarkers(mode, o, series) {
if (mode === o) {
return;
}
const elements = this.seriesElements.get(series);
if (elements.alarmSet) {
elements.alarmSet.destroy();
this.alarmSets.splice(this.alarmSets.indexOf(elements.alarmSet), 1);
}
elements.alarmSet = this.alarmPointSetForSeries(series);
if (elements.alarmSet) {
this.alarmSets.push(elements.alarmSet);
}
},
changeMarkers(mode, o, series) {
if (mode === o) {
return;
}
const elements = this.seriesElements.get(series);
elements.pointSets.forEach(function (pointSet) {
this.pointSets.splice(this.pointSets.indexOf(pointSet), 1);
pointSet.destroy();
}, this);
elements.pointSets = [];
const pointSet = this.pointSetForSeries(series);
if (pointSet) {
elements.pointSets.push(pointSet);
this.pointSets.push(pointSet);
}
},
changeLimitLines(mode, o, series) {
if (mode === o) {
return;
}
this.makeLimitLines(series);
this.updateLimitsAndDraw();
},
resetAxisAndRedraw(newYAxisId, oldYAxisId, series) {
if (!oldYAxisId) {
return;
}
//Remove the old chart elements for the series since their offsets are pointing to the old y axis
this.removeChartElement(series);
this.resetYOffsetAndSeriesDataForYAxis(oldYAxisId);
//Make the chart elements again for the new y-axis and offset
this.makeChartElement(series);
this.makeLimitLines(series);
this.scheduleDraw();
},
onSeriesRemove(series) {
this.stopListening(series);
this.removeChartElement(series);
this.scheduleDraw();
},
destroy() {
this.isDestroyed = true;
this.stopListening();
this.lines.forEach(line => line.destroy());
this.limitLines.forEach(line => line.destroy());
DrawLoader.releaseDrawAPI(this.drawAPI);
},
resetYOffsetAndSeriesDataForYAxis(yAxisId) {
delete this.offset[yAxisId].y;
delete this.offset[yAxisId].xVal;
delete this.offset[yAxisId].yVal;
delete this.offset[yAxisId].xKey;
delete this.offset[yAxisId].yKey;
this.resetResetChartElements(yAxisId);
},
resetResetChartElements(yAxisId) {
const lines = this.lines.filter(this.matchByYAxisIdExcludingVisibility.bind(this, yAxisId));
lines.forEach(function (line) {
line.reset();
});
const limitLines = this.limitLines.filter(this.matchByYAxisIdExcludingVisibility.bind(this, yAxisId));
limitLines.forEach(function (line) {
line.reset();
});
const pointSets = this.pointSets.filter(this.matchByYAxisIdExcludingVisibility.bind(this, yAxisId));
pointSets.forEach(function (pointSet) {
pointSet.reset();
});
},
setOffset(offsetPoint, index, series) {
const mainYAxisId = this.config.yAxis.get('id');
const yAxisId = series.get('yAxisId') || mainYAxisId;
if (this.offset[yAxisId].x && this.offset[yAxisId].y) {
return;
}
const offsets = {
x: series.getXVal(offsetPoint),
y: series.getYVal(offsetPoint)
};
this.offset[yAxisId].x = function (x) {
return x - offsets.x;
}.bind(this);
this.offset[yAxisId].y = function (y) {
return y - offsets.y;
}.bind(this);
this.offset[yAxisId].xVal = function (point, pSeries) {
return this.offset[yAxisId].x(pSeries.getXVal(point));
}.bind(this);
this.offset[yAxisId].yVal = function (point, pSeries) {
return this.offset[yAxisId].y(pSeries.getYVal(point));
}.bind(this);
},
initializeCanvas(canvas, overlay) {
this.canvas = canvas;
this.overlay = overlay;
this.drawAPI = DrawLoader.getDrawAPI(canvas, overlay);
if (this.drawAPI) {
this.listenTo(this.drawAPI, 'error', this.fallbackToCanvas, this);
}
return Boolean(this.drawAPI);
},
fallbackToCanvas() {
this.stopListening(this.drawAPI);
DrawLoader.releaseDrawAPI(this.drawAPI);
// Have to throw away the old canvas elements and replace with new
// canvas elements in order to get new drawing contexts.
const div = document.createElement('div');
div.innerHTML = this.canvasTemplate + this.canvasTemplate;
const mainCanvas = div.querySelectorAll("canvas")[1];
const overlayCanvas = div.querySelectorAll("canvas")[0];
this.canvas.parentNode.replaceChild(mainCanvas, this.canvas);
this.canvas = mainCanvas;
this.overlay.parentNode.replaceChild(overlayCanvas, this.overlay);
this.overlay = overlayCanvas;
this.drawAPI = DrawLoader.getFallbackDrawAPI(this.canvas, this.overlay);
this.$emit('plotReinitializeCanvas');
},
removeChartElement(series) {
const elements = this.seriesElements.get(series);
elements.lines.forEach(function (line) {
this.lines.splice(this.lines.indexOf(line), 1);
line.destroy();
}, this);
elements.pointSets.forEach(function (pointSet) {
this.pointSets.splice(this.pointSets.indexOf(pointSet), 1);
pointSet.destroy();
}, this);
if (elements.alarmSet) {
elements.alarmSet.destroy();
this.alarmSets.splice(this.alarmSets.indexOf(elements.alarmSet), 1);
}
this.seriesElements.delete(series);
this.clearLimitLines(series);
},
lineForSeries(series) {
const mainYAxisId = this.config.yAxis.get('id');
const yAxisId = series.get('yAxisId') || mainYAxisId;
let offset = this.offset[yAxisId];
if (series.get('interpolate') === 'linear') {
return new MCTChartLineLinear(
series,
this,
offset
);
}
if (series.get('interpolate') === 'stepAfter') {
return new MCTChartLineStepAfter(
series,
this,
offset
);
}
},
limitLineForSeries(series) {
const mainYAxisId = this.config.yAxis.get('id');
const yAxisId = series.get('yAxisId') || mainYAxisId;
let offset = this.offset[yAxisId];
return new MCTChartAlarmLineSet(
series,
this,
offset,
this.openmct.time.bounds()
);
},
pointSetForSeries(series) {
const mainYAxisId = this.config.yAxis.get('id');
const yAxisId = series.get('yAxisId') || mainYAxisId;
let offset = this.offset[yAxisId];
if (series.get('markers')) {
return new MCTChartPointSet(
series,
this,
offset
);
}
},
alarmPointSetForSeries(series) {
const mainYAxisId = this.config.yAxis.get('id');
const yAxisId = series.get('yAxisId') || mainYAxisId;
let offset = this.offset[yAxisId];
if (series.get('alarmMarkers')) {
return new MCTChartAlarmPointSet(
series,
this,
offset
);
}
},
makeChartElement(series) {
const elements = {
lines: [],
pointSets: [],
limitLines: []
};
const line = this.lineForSeries(series);
if (line) {
elements.lines.push(line);
this.lines.push(line);
}
const pointSet = this.pointSetForSeries(series);
if (pointSet) {
elements.pointSets.push(pointSet);
this.pointSets.push(pointSet);
}
elements.alarmSet = this.alarmPointSetForSeries(series);
if (elements.alarmSet) {
this.alarmSets.push(elements.alarmSet);
}
this.seriesElements.set(series, elements);
},
makeLimitLines(series) {
this.clearLimitLines(series);
if (!series || !series.get('limitLines')) {
return;
}
const limitElements = {
limitLines: []
};
const limitLine = this.limitLineForSeries(series);
if (limitLine) {
limitElements.limitLines.push(limitLine);
this.limitLines.push(limitLine);
}
this.seriesLimits.set(series, limitElements);
},
clearLimitLines(series) {
const seriesLimits = this.seriesLimits.get(series);
if (seriesLimits) {
seriesLimits.limitLines.forEach(function (line) {
this.limitLines.splice(this.limitLines.indexOf(line), 1);
line.destroy();
}, this);
this.seriesLimits.delete(series);
}
},
canDraw(yAxisId) {
if (!this.offset[yAxisId] || !this.offset[yAxisId].x || !this.offset[yAxisId].y) {
return false;
}
return true;
},
updateLimitsAndDraw() {
this.drawLimitLines();
this.scheduleDraw();
},
scheduleDraw() {
if (!this.drawScheduled) {
requestAnimationFrame(this.draw);
this.drawScheduled = true;
}
},
draw() {
this.drawScheduled = false;
if (this.isDestroyed) {
return;
}
this.drawAPI.clear();
const mainYAxisId = this.config.yAxis.get('id');
//There has to be at least one yAxis
const yAxisIds = [mainYAxisId].concat(this.config.additionalYAxes.map(yAxis => yAxis.get('id')));
// Repeat drawing for all yAxes
yAxisIds.forEach((id) => {
if (this.canDraw(id)) {
this.updateViewport(id);
this.drawSeries(id);
this.drawRectangles(id);
this.drawHighlights(id);
// only draw these in fixed time mode or plot is paused
if (this.annotationViewingAndEditingAllowed) {
this.drawAnnotatedPoints(id);
this.drawAnnotationSelections(id);
}
}
});
},
updateViewport(yAxisId) {
const mainYAxisId = this.config.yAxis.get('id');
const xRange = this.config.xAxis.get('displayRange');
let yRange;
if (yAxisId === mainYAxisId) {
yRange = this.config.yAxis.get('displayRange');
} else {
if (this.config.additionalYAxes.length) {
const yAxisForId = this.config.additionalYAxes.find(yAxis => yAxis.get('id') === yAxisId);
yRange = yAxisForId.get('displayRange');
}
}
if (!xRange || !yRange) {
return;
}
const dimensions = [
xRange.max - xRange.min,
yRange.max - yRange.min
];
let origin;
origin = [
this.offset[yAxisId].x(xRange.min),
this.offset[yAxisId].y(yRange.min)
];
this.drawAPI.setDimensions(
dimensions,
origin
);
},
// match items by their yAxisId, but don't care if the series is hidden or not.
matchByYAxisIdExcludingVisibility() {
const args = Array.from(arguments).slice(0, 4);
return this.matchByYAxisId(...args, true);
},
matchByYAxisId(id, item, index, items, excludeVisibility = false) {
const mainYAxisId = this.config.yAxis.get('id');
let matchesId = false;
const axisSeriesAreVisible = excludeVisibility || this.hiddenYAxisIds.indexOf(id) < 0;
const series = item.series;
if (axisSeriesAreVisible && series) {
const seriesYAxisId = series.get('yAxisId') || mainYAxisId;
matchesId = seriesYAxisId === id;
}
return matchesId;
},
drawSeries(id) {
const lines = this.lines.filter(this.matchByYAxisId.bind(this, id));
lines.forEach(this.drawLine, this);
const pointSets = this.pointSets.filter(this.matchByYAxisId.bind(this, id));
pointSets.forEach(this.drawPoints, this);
const alarmSets = this.alarmSets.filter(this.matchByYAxisId.bind(this, id));
alarmSets.forEach(this.drawAlarmPoints, this);
},
drawLimitLines() {
Array.from(this.$refs.limitArea.children).forEach((el) => el.remove());
this.config.series.models.forEach(series => {
const yAxisId = series.get('yAxisId');
if (this.hiddenYAxisIds.indexOf(yAxisId) < 0) {
this.drawLimitLinesForSeries(yAxisId, series);
}
});
},
drawLimitLinesForSeries(yAxisId, series) {
if (!this.canDraw(yAxisId)) {
return;
}
this.updateViewport(yAxisId);
if (!this.drawAPI.origin) {
return;
}
let limitPointOverlap = [];
this.limitLines.forEach((limitLine) => {
let limitContainerEl = this.$refs.limitArea;
limitLine.limits.forEach((limit) => {
if (series.keyString !== limit.seriesKey) {
return;
}
const showLabels = this.showLabels(limit.seriesKey);
if (showLabels) {
const overlap = this.getLimitOverlap(limit, limitPointOverlap);
limitPointOverlap.push(overlap);
let limitLabelEl = this.getLimitLabel(limit, overlap);
limitContainerEl.appendChild(limitLabelEl);
}
let limitEl = this.getLimitElement(limit);
limitContainerEl.appendChild(limitEl);
}, this);
});
},
showLabels(seriesKey) {
return this.showLimitLineLabels.seriesKey
&& (this.showLimitLineLabels.seriesKey === seriesKey);
},
getLimitElement(limit) {
let point = {
left: 0,
top: this.drawAPI.y(limit.point.y)
};
let LimitLineClass = Vue.extend(LimitLine);
const component = new LimitLineClass({
propsData: {
point,
limit
}
});
component.$mount();
return component.$el;
},
getLimitOverlap(limit, overlapMap) {
//calculate if limit lines are too close to each other
let limitTop = this.drawAPI.y(limit.point.y);
const needsVerticalAdjustment = limitTop - CLEARANCE <= 0;
let needsHorizontalAdjustment = false;
overlapMap.forEach(value => {
let diffTop;
if (limitTop > value.overlapTop) {
diffTop = limitTop - value.overlapTop;
} else {
diffTop = value.overlapTop - limitTop;
}
//need to compare +ves to +ves and -ves to -ves
if (!needsHorizontalAdjustment
&& Math.abs(diffTop) <= CLEARANCE
&& value.needsHorizontalAdjustment !== true) {
needsHorizontalAdjustment = true;
}
});
return {
needsHorizontalAdjustment,
needsVerticalAdjustment,
overlapTop: limitTop
};
},
getLimitLabel(limit, overlap) {
let point = {
left: 0,
top: this.drawAPI.y(limit.point.y)
};
let LimitLabelClass = Vue.extend(LimitLabel);
const component = new LimitLabelClass({
propsData: {
limit: Object.assign({}, overlap, limit),
point
}
});
component.$mount();
return component.$el;
},
drawAlarmPoints(alarmSet) {
this.drawAPI.drawLimitPoints(
alarmSet.points,
alarmSet.series.get('color').asRGBAArray(),
alarmSet.series.get('markerSize')
);
},
drawPoints(chartElement) {
this.drawAPI.drawPoints(
chartElement.getBuffer(),
chartElement.color().asRGBAArray(),
chartElement.count,
chartElement.series.get('markerSize'),
chartElement.series.get('markerShape')
);
},
drawLine(chartElement, disconnected) {
if (chartElement) {
this.drawAPI.drawLine(
chartElement.getBuffer(),
chartElement.color().asRGBAArray(),
chartElement.count,
disconnected
);
}
},
annotatedPointWithinRange(annotatedPoint, xRange, yRange) {
if (!yRange) {
return false;
}
const xValue = annotatedPoint.series.getXVal(annotatedPoint.point);
const yValue = annotatedPoint.series.getYVal(annotatedPoint.point);
return ((xValue > xRange.min) && (xValue < xRange.max)
&& (yValue > yRange.min) && (yValue < yRange.max));
},
drawAnnotatedPoints(yAxisId) {
// we should do this by series, and then plot all the points at once instead
// of doing it one by one
if (this.annotatedPoints && this.annotatedPoints.length) {
const uniquePointsToDraw = [];
const xRange = this.config.xAxis.get('displayRange');
let yRange;
if (yAxisId === this.config.yAxis.get('id')) {
yRange = this.config.yAxis.get('displayRange');
} else if (this.config.additionalYAxes.length) {
const yAxisForId = this.config.additionalYAxes.find(yAxis => yAxis.get('id') === yAxisId);
yRange = yAxisForId.get('displayRange');
}
const annotatedPoints = this.annotatedPoints.filter(this.matchByYAxisId.bind(this, yAxisId));
annotatedPoints.forEach((annotatedPoint) => {
// if the annotation is outside the range, don't draw it
if (this.annotatedPointWithinRange(annotatedPoint, xRange, yRange)) {
const canvasXValue = this.offset[yAxisId].xVal(annotatedPoint.point, annotatedPoint.series);
const canvasYValue = this.offset[yAxisId].yVal(annotatedPoint.point, annotatedPoint.series);
const pointToDraw = new Float32Array([canvasXValue, canvasYValue]);
const drawnPoint = uniquePointsToDraw.some((rawPoint) => {
return rawPoint[0] === pointToDraw[0] && rawPoint[1] === pointToDraw[1];
});
if (!drawnPoint) {
uniquePointsToDraw.push(pointToDraw);
this.drawAnnotatedPoint(annotatedPoint, pointToDraw);
}
}
});
}
},
drawAnnotatedPoint(annotatedPoint, pointToDraw) {
if (annotatedPoint.point && annotatedPoint.series) {
const color = annotatedPoint.series.get('color').asRGBAArray();
// set transparency
color[3] = 0.15;
const pointCount = 1;
const shape = annotatedPoint.series.get('markerShape');
this.drawAPI.drawPoints(pointToDraw, color, pointCount, ANNOTATION_SIZE, shape);
}
},
drawAnnotationSelections(yAxisId) {
if (this.annotationSelections && this.annotationSelections.length) {
const annotationSelections = this.annotationSelections.filter(this.matchByYAxisId.bind(this, yAxisId));
annotationSelections.forEach(this.drawAnnotationSelection.bind(this, yAxisId), this);
}
},
drawAnnotationSelection(yAxisId, annotationSelection) {
const points = new Float32Array([
this.offset[yAxisId].xVal(annotationSelection.point, annotationSelection.series),
this.offset[yAxisId].yVal(annotationSelection.point, annotationSelection.series)
]);
const color = [255, 255, 255, 1]; // white
const pointCount = 1;
const shape = annotationSelection.series.get('markerShape');
this.drawAPI.drawPoints(points, color, pointCount, ANNOTATION_SIZE, shape);
},
drawHighlights(yAxisId) {
if (this.highlights && this.highlights.length) {
const highlights = this.highlights.filter(this.matchByYAxisId.bind(this, yAxisId));
highlights.forEach(this.drawHighlight.bind(this, yAxisId), this);
}
},
drawHighlight(yAxisId, highlight) {
const points = new Float32Array([
this.offset[yAxisId].xVal(highlight.point, highlight.series),
this.offset[yAxisId].yVal(highlight.point, highlight.series)
]);
const color = highlight.series.get('color').asRGBAArray();
const pointCount = 1;
const shape = highlight.series.get('markerShape');
this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE, shape);
},
drawRectangles(yAxisId) {
if (this.rectangles) {
this.rectangles.forEach(this.drawRectangle.bind(this, yAxisId), this);
}
},
drawRectangle(yAxisId, rect) {
if (!rect.start.yAxisIds || !rect.end.yAxisIds) {
return;
}
const startYIndex = rect.start.yAxisIds.findIndex(id => id === yAxisId);
const endYIndex = rect.end.yAxisIds.findIndex(id => id === yAxisId);
if (rect.start.y[startYIndex] && rect.end.y[endYIndex]) {
this.drawAPI.drawSquare(
[
this.offset[yAxisId].x(rect.start.x),
this.offset[yAxisId].y(rect.start.y[startYIndex])
],
[
this.offset[yAxisId].x(rect.end.x),
this.offset[yAxisId].y(rect.end.y[endYIndex])
],
rect.color
);
}
}
}
};
</script>