diff --git a/src/plugins/plot/res/templates/mct-plot.html b/src/plugins/plot/res/templates/mct-plot.html index 2f275a9cb8..f0d9e4a616 100644 --- a/src/plugins/plot/res/templates/mct-plot.html +++ b/src/plugins/plot/res/templates/mct-plot.html @@ -240,9 +240,9 @@ -
+
-
+
{{ xAxis.get('label') }}
+ +
diff --git a/src/plugins/plot/src/chart/MCTChartController.js b/src/plugins/plot/src/chart/MCTChartController.js index 096af92aa8..c7899311bc 100644 --- a/src/plugins/plot/src/chart/MCTChartController.js +++ b/src/plugins/plot/src/chart/MCTChartController.js @@ -66,7 +66,6 @@ function ( this.listenTo(this.config.series, 'add', this.onSeriesAdd, this); this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this); this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this); - this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this); this.listenTo(this.config.yAxis, 'change', this.scheduleDraw); this.listenTo(this.config.xAxis, 'change', this.scheduleDraw); this.$scope.$watch('highlights', this.scheduleDraw); @@ -79,7 +78,14 @@ function ( MCTChartController.$inject = ['$scope']; + MCTChartController.prototype.reDraw = function (mode, o, series) { + this.changeInterpolate(mode, o, series); + this.changeMarkers(mode, o, series); + this.changeAlarmMarkers(mode, o, series); + }; + MCTChartController.prototype.onSeriesAdd = function (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); diff --git a/src/plugins/plot/src/configuration/PlotSeries.js b/src/plugins/plot/src/configuration/PlotSeries.js index 4225a695fb..ab52dd11d3 100644 --- a/src/plugins/plot/src/configuration/PlotSeries.js +++ b/src/plugins/plot/src/configuration/PlotSeries.js @@ -428,6 +428,19 @@ define([ this.filters = deepCopiedFilters; } }, + getDisplayRange: function (xKey) { + const unsortedData = this.data; + this.data = []; + unsortedData.forEach(point => this.add(point, false)); + + const minValue = this.getXVal(this.data[0]); + const maxValue = this.getXVal(this.data[this.data.length - 1]); + + return { + min: minValue, + max: maxValue + }; + }, markerOptionsDisplayText: function () { const showMarkers = this.get('markers'); if (!showMarkers) { diff --git a/src/plugins/plot/src/configuration/XAxisModel.js b/src/plugins/plot/src/configuration/XAxisModel.js index bbd99aff30..ccdfdde252 100644 --- a/src/plugins/plot/src/configuration/XAxisModel.js +++ b/src/plugins/plot/src/configuration/XAxisModel.js @@ -49,6 +49,7 @@ define([ } this.listenTo(this, 'change:key', this.changeKey, this); + this.listenTo(this, 'resetSeries', this.resetSeries, this); }, changeKey: function (newKey) { const series = this.plot.series.first(); @@ -66,6 +67,10 @@ define([ this.plot.series.forEach(function (plotSeries) { plotSeries.set('xKey', newKey); + }); + }, + resetSeries: function () { + this.plot.series.forEach(function (plotSeries) { plotSeries.reset(); }); }, diff --git a/src/plugins/plot/src/plot/MCTPlotController.js b/src/plugins/plot/src/plot/MCTPlotController.js index 904c27ee9b..5f66e0b3eb 100644 --- a/src/plugins/plot/src/plot/MCTPlotController.js +++ b/src/plugins/plot/src/plot/MCTPlotController.js @@ -102,12 +102,32 @@ define([ this.listenTo(this.$scope, 'plot:tickWidth', this.onTickWidthChange, this); this.listenTo(this.$scope, 'plot:highlight:set', this.onPlotHighlightSet, this); this.listenTo(this.$scope, 'plot:reinitializeCanvas', this.initCanvas, this); + this.listenTo(this.config.xAxis, 'resetSeries', this.setUpXAxisOptions, this); this.listenTo(this.config.xAxis, 'change:displayRange', this.onXAxisChange, this); this.listenTo(this.config.yAxis, 'change:displayRange', this.onYAxisChange, this); + this.setUpXAxisOptions(); this.setUpYAxisOptions(); }; + MCTPlotController.prototype.setUpXAxisOptions = function () { + const xAxisKey = this.config.xAxis.get('key'); + + if (this.$scope.series.length === 1) { + let metadata = this.$scope.series[0].metadata; + + this.$scope.xKeyOptions = metadata + .valuesForHints(['domain']) + .map(function (o) { + return { + name: o.name, + key: o.key + }; + }); + this.$scope.selectedXKeyOption = this.getXKeyOption(xAxisKey); + } + }; + MCTPlotController.prototype.setUpYAxisOptions = function () { if (this.$scope.series.length === 1) { let metadata = this.$scope.series[0].metadata; @@ -534,6 +554,32 @@ define([ this.cursorGuide = !this.cursorGuide; }; + MCTPlotController.prototype.getXKeyOption = function (key) { + return this.$scope.xKeyOptions.find(option => option.key === key); + }; + + MCTPlotController.prototype.isEnabledXKeyToggle = function () { + const isSinglePlot = this.$scope.xKeyOptions && this.$scope.xKeyOptions.length > 1 && this.$scope.series.length === 1; + const isFrozen = this.config.xAxis.get('frozen'); + const inRealTimeMode = this.config.openmct.time.clock(); + + return isSinglePlot && !isFrozen && !inRealTimeMode; + }; + + MCTPlotController.prototype.toggleXKeyOption = function (lastXKey, series) { + const selectedXKey = this.$scope.selectedXKeyOption.key; + const dataForSelectedXKey = series.data + ? series.data[0][selectedXKey] + : undefined; + + if (dataForSelectedXKey !== undefined) { + this.config.xAxis.set('key', selectedXKey); + } else { + this.config.openmct.notifications.error('Cannot change x-axis view as no data exists for this view type.'); + this.$scope.selectedXKeyOption.key = lastXKey; + } + }; + MCTPlotController.prototype.toggleYAxisLabel = function (label, options, series) { let yAxisObject = options.filter(o => o.name === label)[0]; diff --git a/src/plugins/plot/src/plot/MCTTicksController.js b/src/plugins/plot/src/plot/MCTTicksController.js index 31258158ae..ffcb0ce935 100644 --- a/src/plugins/plot/src/plot/MCTTicksController.js +++ b/src/plugins/plot/src/plot/MCTTicksController.js @@ -126,6 +126,7 @@ define([ this.tickUpdate = false; this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this); this.listenTo(this.axis, 'change:format', this.updateTicks, this); + this.listenTo(this.axis, 'change:key', this.updateTicksForceRegeneration, this); this.listenTo(this.$scope, '$destroy', this.stopListening, this); this.updateTicks(); }; @@ -137,12 +138,19 @@ define([ /** * Determine whether ticks should be regenerated for a given range. - * Ticks are updated a) if they don't exist, b) if the existing ticks are - * outside of given range, or c) if the range exceeds the size of the tick - * range by more than one tick step. + * Ticks are updated + * a) if they don't exist, + * b) if existing ticks are outside of given range, + * c) if range exceeds size of tick range by more than one tick step, + * d) if forced to regenerate (ex. changing x-axis metadata). + * * @private */ - MCTTicksController.prototype.shouldRegenerateTicks = function (range) { + MCTTicksController.prototype.shouldRegenerateTicks = function (range, forceRegeneration) { + if (forceRegeneration) { + return true; + } + if (!this.tickRange || !this.$scope.ticks || !this.$scope.ticks.length) { return true; } @@ -175,7 +183,11 @@ define([ return ticks(range.min, range.max, number); }; - MCTTicksController.prototype.updateTicks = function () { + MCTTicksController.prototype.updateTicksForceRegeneration = function () { + this.updateTicks(true); + }; + + MCTTicksController.prototype.updateTicks = function (forceRegeneration = false) { const range = this.axis.get('displayRange'); if (!range) { delete this.$scope.min; @@ -196,7 +208,7 @@ define([ this.$scope.min = range.min; this.$scope.max = range.max; this.$scope.interval = Math.abs(range.min - range.max); - if (this.shouldRegenerateTicks(range)) { + if (this.shouldRegenerateTicks(range, forceRegeneration)) { let newTicks = this.getTicks(); this.tickRange = { min: Math.min.apply(Math, newTicks), diff --git a/src/plugins/plot/src/telemetry/PlotController.js b/src/plugins/plot/src/telemetry/PlotController.js index 2981275aa4..3536f82434 100644 --- a/src/plugins/plot/src/telemetry/PlotController.js +++ b/src/plugins/plot/src/telemetry/PlotController.js @@ -90,7 +90,7 @@ define([ PlotController.prototype.followTimeConductor = function () { this.listenTo(this.openmct.time, 'bounds', this.updateDisplayBounds, this); - this.listenTo(this.openmct.time, 'timeSystem', this.onTimeSystemChange, this); + this.listenTo(this.openmct.time, 'timeSystem', this.syncXAxisToTimeSystem, this); this.synchronized(true); }; @@ -134,6 +134,9 @@ define([ }; PlotController.prototype.addSeries = function (series) { + this.listenTo(series, 'change:xKey', (xKey) => { + this.setDisplayRange(series, xKey); + }, this); this.listenTo(series, 'change:yKey', () => { this.loadSeriesData(series); }, this); @@ -145,6 +148,15 @@ define([ this.loadSeriesData(series); }; + PlotController.prototype.setDisplayRange = function (series, xKey) { + if (this.config.series.models.length !== 1) { + return; + } + + const displayRange = series.getDisplayRange(xKey); + this.config.xAxis.set('range', displayRange); + }; + PlotController.prototype.removeSeries = function (plotSeries) { this.stopListening(plotSeries); }; @@ -165,8 +177,9 @@ define([ return config; }; - PlotController.prototype.onTimeSystemChange = function (timeSystem) { + PlotController.prototype.syncXAxisToTimeSystem = function (timeSystem) { this.config.xAxis.set('key', timeSystem.key); + this.config.xAxis.emit('resetSeries'); }; PlotController.prototype.destroy = function () { @@ -189,7 +202,8 @@ define([ plotSeries.load({ size: this.$element[0].offsetWidth, start: range.min, - end: range.max + end: range.max, + domain: this.config.xAxis.get('key') }) .then(this.stopLoading()); if (purge) { @@ -202,10 +216,18 @@ define([ * Track latest display bounds. Forces update when not receiving ticks. */ PlotController.prototype.updateDisplayBounds = function (bounds, isTick) { + + const xAxisKey = this.config.xAxis.get('key'); + const timeSystem = this.openmct.time.timeSystem(); const newRange = { min: bounds.start, max: bounds.end }; + + if (xAxisKey !== timeSystem.key) { + this.syncXAxisToTimeSystem(timeSystem); + } + this.config.xAxis.set('range', newRange); if (!isTick) { this.skipReloadOnInteraction = true; diff --git a/src/styles/_legacy-plots.scss b/src/styles/_legacy-plots.scss index 8b28ec8bcb..9986c5bfc5 100644 --- a/src/styles/_legacy-plots.scss +++ b/src/styles/_legacy-plots.scss @@ -27,7 +27,7 @@ mct-plot { /*********************** STACKED PLOT LAYOUT */ .is-editing { .gl-plot.child-frame { - &:hover { + @include hover { background: rgba($editUIColorBg, 0.1); box-shadow: inset rgba($editUIColorBg, 0.3) 0 0 0 1px; } @@ -52,6 +52,7 @@ mct-plot { .c-control-bar { display: none; } + .gl-plot-x-label__select, .gl-plot-y-label__select { display: none; } @@ -172,6 +173,14 @@ mct-plot { } } } + + &.gl-plot-x { + @include hover { + .gl-plot-x-label__select { + display: block; + } + } + } } .gl-plot-coords { @@ -217,11 +226,19 @@ mct-plot { } } + .gl-plot-x-label__select { + position: absolute; + left: 50%; + bottom: 0; + transform: translateX(-50%); + z-index: 10; + } + .gl-plot-y-label__select { position: absolute; top: 50%; transform: translateY(-50%); - left: 20px; + left: 0; z-index: 10; }