Plots - remove legacy code (#3814)

* Remove Lecacy plot code
* Adds tests for plots inspector
* Set range max and min to undefined instead of 0
This commit is contained in:
Shefali Joshi 2021-05-20 09:08:50 -07:00 committed by GitHub
parent a83ee1f90f
commit 605eeff9d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
109 changed files with 206 additions and 7254 deletions

View File

@ -88,7 +88,6 @@
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.PlanLayout());
openmct.install(openmct.plugins.Timeline());
openmct.install(openmct.plugins.PlotVue());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.AutoflowView({
type: "telemetry.panel"

View File

@ -36,7 +36,8 @@ define([
'./views/installLegacyViews',
'./policies/LegacyCompositionPolicyAdapter',
'./actions/LegacyActionAdapter',
'./services/LegacyPersistenceAdapter'
'./services/LegacyPersistenceAdapter',
'./services/ExportImageService'
], function (
ActionDialogDecorator,
AdapterCapability,
@ -53,7 +54,8 @@ define([
installLegacyViews,
legacyCompositionPolicyAdapter,
LegacyActionAdapter,
LegacyPersistenceAdapter
LegacyPersistenceAdapter,
ExportImageService
) {
return {
name: 'src/adapter',
@ -82,6 +84,13 @@ define([
"identifierService",
"cacheService"
]
},
{
"key": "exportImageService",
"implementation": ExportImageService,
"depends": [
"dialogService"
]
}
],
components: [

View File

@ -24,13 +24,6 @@ define([
return false;
}
//TODO: Remove this when plots Angular implementation is deprecated
let parent = selection[0].length > 1 && selection[0][1].context.item;
if (parent && parent.type === 'time-strip') {
return (selectionContext.item.type === typeDefinition.key)
&& (typeDefinition.key !== 'telemetry.plot.overlay');
}
return selectionContext.item.type === typeDefinition.key;
},
view: function (selection) {

View File

@ -486,6 +486,12 @@ ObjectAPI.prototype.getOriginalPath = function (identifier, path = []) {
});
};
ObjectAPI.prototype.isObjectPathToALink = function (domainObject, objectPath) {
return objectPath !== undefined
&& objectPath.length > 1
&& domainObject.location !== this.makeKeyString(objectPath[1].identifier);
};
/**
* Uniquely identifies a domain object.
*

View File

@ -65,8 +65,7 @@
<div ref="chartContainer"
class="gl-plot-chart-wrapper"
>
<mct-chart :series-config="config"
:rectangles="rectangles"
<mct-chart :rectangles="rectangles"
:highlights="highlights"
@plotReinitializeCanvas="initCanvas"
/>

View File

@ -48,7 +48,7 @@ export default function PlotViewProvider(openmct) {
name: 'Plot',
cssClass: 'icon-telemetry',
canView(domainObject, objectPath) {
return isCompactView(objectPath) && hasTelemetry(domainObject, openmct);
return hasTelemetry(domainObject, openmct);
},
view: function (domainObject, objectPath) {

View File

@ -166,7 +166,6 @@ export default class YAxisModel extends Model {
* Update yAxis format, values, and label from known series.
*/
updateFromSeries(series) {
this.unset('displayRange');
const plotModel = this.plot.get('domainObject');
const label = _.get(plotModel, 'configuration.yAxis.label');
const sampleSeries = series.first();

View File

@ -31,8 +31,8 @@
</template>
<script>
import PlotOptionsBrowse from "@/plugins/plot/vue/inspector/PlotOptionsBrowse.vue";
import PlotOptionsEdit from "@/plugins/plot/vue/inspector/PlotOptionsEdit.vue";
import PlotOptionsBrowse from "./PlotOptionsBrowse.vue";
import PlotOptionsEdit from "./PlotOptionsEdit.vue";
export default {
components: {
PlotOptionsBrowse,

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
<template>
<div v-if="config && loaded"
<div v-if="loaded"
class="js-plot-options-browse"
>
<ul class="c-tree">
@ -30,7 +30,9 @@
:series="series"
/>
</ul>
<div class="grid-properties">
<div v-if="plotSeries.length"
class="grid-properties"
>
<ul class="l-inspector-part">
<h2 title="Y axis settings for this object">Y Axis</h2>
<li class="grid-row">
@ -113,8 +115,8 @@
<script>
import PlotOptionsItem from "./PlotOptionsItem.vue";
import configStore from "../single/configuration/configStore";
import eventHelpers from "../single/lib/eventHelpers";
import configStore from "../configuration/configStore";
import eventHelpers from "../lib/eventHelpers";
export default {
components: {
@ -123,7 +125,7 @@ export default {
inject: ['openmct', 'domainObject'],
data() {
return {
config: undefined,
config: {},
label: '',
autoscale: '',
autoscalePadding: '',
@ -145,8 +147,8 @@ export default {
mounted() {
eventHelpers.extend(this);
this.config = this.getConfig();
this.initConfiguration();
this.registerListeners();
this.initConfiguration();
this.loaded = true;
},
beforeDestroy() {
@ -186,7 +188,8 @@ export default {
},
addSeries(series, index) {
this.plotSeries[index] = series;
this.$set(this.plotSeries, index, series);
this.initConfiguration();
},
resetAllSeries() {

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
<template>
<div v-if="config && loaded"
<div v-if="loaded"
class="js-plot-options-edit"
>
<ul class="c-tree">
@ -31,13 +31,13 @@
<series-form :series="series" />
</li>
</ul>
<y-axis-form v-show="!!plotSeries.length"
<y-axis-form v-if="plotSeries.length"
class="grid-properties"
:y-axis="config.yAxis"
/>
<ul class="l-inspector-part">
<h2 title="Legend options">Legend</h2>
<legend-form v-show="!!plotSeries.length"
<legend-form v-if="plotSeries.length"
class="grid-properties"
:legend="config.legend"
/>
@ -45,11 +45,11 @@
</div>
</template>
<script>
import SeriesForm from "@/plugins/plot/vue/inspector/forms/SeriesForm.vue";
import YAxisForm from "@/plugins/plot/vue/inspector/forms/YAxisForm.vue";
import LegendForm from "@/plugins/plot/vue/inspector/forms/LegendForm.vue";
import eventHelpers from "@/plugins/plot/vue/single/lib/eventHelpers";
import configStore from "@/plugins/plot/vue/single/configuration/configStore";
import SeriesForm from "./forms/SeriesForm.vue";
import YAxisForm from "./forms/YAxisForm.vue";
import LegendForm from "./forms/LegendForm.vue";
import eventHelpers from "../lib/eventHelpers";
import configStore from "../configuration/configStore";
export default {
components: {
@ -88,7 +88,7 @@ export default {
},
addSeries(series, index) {
this.plotSeries[index] = series;
this.$set(this.plotSeries, index, series);
},
resetAllSeries() {

View File

@ -1,6 +1,8 @@
<template>
<ul>
<li class="c-tree__item menus-to-left">
<li class="c-tree__item menus-to-left"
:class="isAliasClass"
>
<span class="c-disclosure-triangle is-enabled flex-elem"
:class="expandedCssClass"
@click="toggleExpanded"
@ -78,7 +80,7 @@
<script>
export default {
inject: ['openmct'],
inject: ['openmct', 'domainObject', 'path'],
props: {
series: {
type: Object,
@ -93,17 +95,20 @@ export default {
};
},
computed: {
getSeriesClass() {
isAliasClass() {
let cssClass = '';
let legacyObject = this.openmct.legacyObject(this.series.domainObject);
let location = legacyObject.getCapability('location');
if (location && location.isLink()) {
cssClass = 'l-icon-link';
const domainObjectPath = [this.series.domainObject, ...this.path];
if (this.openmct.objects.isObjectPathToALink(this.series.domainObject, domainObjectPath)) {
cssClass = 'is-alias';
}
let type = legacyObject.getCapability('type');
if (type) {
cssClass = `${cssClass} ${type.getCssClass()}`;
return cssClass;
},
getSeriesClass() {
let cssClass = '';
let type = this.openmct.types.get(this.series.domainObject.type);
if (type.definition.cssClass) {
cssClass = `${cssClass} ${type.definition.cssClass}`;
}
return cssClass;

View File

@ -11,16 +11,20 @@ export default function PlotsInspectorViewProvider(openmct) {
return false;
}
let parent = selection[0].length > 1 && selection[0][1].context.item;
let object = selection[0][0].context.item;
return parent
&& parent.type === 'time-strip'
&& object
return object
&& object.type === 'telemetry.plot.overlay';
},
view: function (selection) {
let component;
let objectPath;
if (selection.length) {
objectPath = selection[0].map((selectionItem) => {
return selectionItem.context.item;
});
}
return {
show: function (element) {
@ -31,7 +35,8 @@ export default function PlotsInspectorViewProvider(openmct) {
},
provide: {
openmct,
domainObject: openmct.selection.get()[0][0].context.item
domainObject: selection[0][0].context.item,
path: objectPath
},
template: '<plot-options></plot-options>'
});

View File

@ -83,7 +83,7 @@
</div>
</template>
<script>
import {coerce, objectPath, validate} from "@/plugins/plot/vue/inspector/forms/formUtil";
import {coerce, objectPath, validate} from "./formUtil";
import _ from "lodash";
export default {

View File

@ -1,6 +1,8 @@
<template>
<ul>
<li class="c-tree__item menus-to-left">
<li class="c-tree__item menus-to-left"
:class="isAliasCss"
>
<span class="c-disclosure-triangle is-enabled flex-elem"
:class="expandedCssClass"
@click="toggleExpanded"
@ -8,7 +10,7 @@
</span>
<div :class="objectLabelCss">
<div class="c-object-label__type-icon"
:class="[seriesCss, linkCss]"
:class="[seriesCss]"
>
<span class="is-status__indicator"
title="This item is missing or suspect"
@ -142,12 +144,12 @@
</template>
<script>
import { MARKER_SHAPES } from "../../single/draw/MarkerShapes";
import { MARKER_SHAPES } from "../../draw/MarkerShapes";
import { objectPath, validate, coerce } from "./formUtil";
import _ from 'lodash';
export default {
inject: ['openmct', 'domainObject'],
inject: ['openmct', 'domainObject', 'path'],
props: {
series: {
type: Object,
@ -179,17 +181,15 @@ export default {
return this.status ? `c-object-label is-status--${this.status}'` : 'c-object-label';
},
seriesCss() {
let legacyObject = this.openmct.legacyObject(this.series.domainObject);
let type = legacyObject.getCapability('type');
let type = this.openmct.types.get(this.series.domainObject.type);
return type ? `c-object-label__type-icon ${type.getCssClass()}` : `c-object-label__type-icon`;
return type.definition.cssClass ? `c-object-label__type-icon ${type.definition.cssClass}` : `c-object-label__type-icon`;
},
linkCss() {
isAliasCss() {
let cssClass = '';
let legacyObject = this.openmct.legacyObject(this.series.domainObject);
let location = legacyObject.getCapability('location');
if (location && location.isLink()) {
cssClass = 'l-icon-link';
const domainObjectPath = [this.series.domainObject, ...this.path];
if (this.openmct.objects.isObjectPathToALink(this.series.domainObject, domainObjectPath)) {
cssClass = 'is-alias';
}
return cssClass;

View File

@ -180,7 +180,10 @@ export default {
this.autoscale = this.yAxis.get('autoscale');
this.autoscalePadding = this.yAxis.get('autoscalePadding');
const range = this.yAxis.get('range');
if (range) {
if (!range) {
this.rangeMin = undefined;
this.rangeMax = undefined;
} else {
this.rangeMin = range.min;
this.rangeMax = range.max;
}

View File

@ -0,0 +1,29 @@
export default function OverlayPlotCompositionPolicy(openmct) {
function hasNumericTelemetry(domainObject) {
const hasTelemetry = openmct.telemetry.isTelemetryObject(domainObject);
if (!hasTelemetry) {
return false;
}
let metadata = openmct.telemetry.getMetadata(domainObject);
return metadata.values().length > 0 && hasDomainAndRange(metadata);
}
function hasDomainAndRange(metadata) {
return (metadata.valuesForHints(['range']).length > 0
&& metadata.valuesForHints(['domain']).length > 0);
}
return {
allow: function (parent, child) {
if (parent.type === 'telemetry.plot.overlay'
&& (hasNumericTelemetry(child) === false)) {
return false;
}
return true;
}
};
}

View File

@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Plot from '../single/Plot.vue';
import Plot from '../Plot.vue';
import Vue from 'vue';
export default function OverlayPlotViewProvider(openmct) {
@ -33,11 +33,11 @@ export default function OverlayPlotViewProvider(openmct) {
name: 'Overlay Plot',
cssClass: 'icon-telemetry',
canView(domainObject, objectPath) {
return isCompactView(objectPath) && domainObject.type === 'telemetry.plot.overlay';
return domainObject.type === 'telemetry.plot.overlay';
},
canEdit(domainObject, objectPath) {
return isCompactView(objectPath) && domainObject.type === 'telemetry.plot.overlay';
return domainObject.type === 'telemetry.plot.overlay';
},
view: function (domainObject, objectPath) {

View File

@ -20,262 +20,52 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
"./src/chart/MCTChartDirective",
"./src/plot/MCTPlotDirective",
'./src/plot/MCTTicksDirective',
"./src/telemetry/MCTOverlayPlot",
"./src/telemetry/PlotController",
"./src/telemetry/StackedPlotController",
"./src/inspector/PlotInspector",
"./src/inspector/PlotOptionsController",
"./src/inspector/PlotLegendFormController",
"./src/inspector/PlotYAxisFormController",
"./src/inspector/PlotSeriesFormController",
"./src/inspector/HideElementPoolDirective",
"./src/services/ExportImageService",
'./src/PlotViewPolicy',
"./res/templates/plot-options.html",
"./res/templates/plot-options-browse.html",
"./res/templates/plot-options-edit.html",
"./res/templates/stacked-plot.html",
"./res/templates/plot.html"
], function (
MCTChartDirective,
MCTPlotDirective,
MCTTicksDirective,
MCTOverlayPlot,
PlotController,
StackedPlotController,
PlotInspector,
PlotOptionsController,
PlotLegendFormController,
PlotYAxisFormController,
PlotSeriesFormController,
HideElementPool,
ExportImageService,
PlotViewPolicy,
plotOptionsTemplate,
plotOptionsBrowseTemplate,
plotOptionsEditTemplate,
StackedPlotTemplate,
PlotTemplate
) {
import PlotViewProvider from './PlotViewProvider';
import OverlayPlotViewProvider from './overlayPlot/OverlayPlotViewProvider';
import StackedPlotViewProvider from './stackedPlot/StackedPlotViewProvider';
import PlotsInspectorViewProvider from './inspector/PlotsInspectorViewProvider';
import OverlayPlotCompositionPolicy from './overlayPlot/OverlayPlotCompositionPolicy';
import StackedPlotCompositionPolicy from './stackedPlot/StackedPlotCompositionPolicy';
let installed = false;
export default function () {
return function install(openmct) {
function PlotPlugin() {
return function install(openmct) {
if (installed) {
return;
}
openmct.types.addType('telemetry.plot.overlay', {
key: "telemetry.plot.overlay",
name: "Overlay Plot",
cssClass: "icon-plot-overlay",
description: "Combine multiple telemetry elements and view them together as a plot with common X and Y axes. Can be added to Display Layouts.",
creatable: "true",
initialize: function (domainObject) {
domainObject.composition = [];
domainObject.configuration = {
series: [],
yAxis: {},
xAxis: {}
};
},
priority: 891
});
installed = true;
openmct.types.addType('telemetry.plot.stacked', {
key: "telemetry.plot.stacked",
name: "Stacked Plot",
cssClass: "icon-plot-stacked",
description: "Combine multiple telemetry elements and view them together as a plot with a common X axis and individual Y axes. Can be added to Display Layouts.",
creatable: true,
initialize: function (domainObject) {
domainObject.composition = [];
domainObject.configuration = {};
},
priority: 890
});
openmct.legacyRegistry.register("openmct/plot", {
"name": "Plot view for telemetry, reborn",
"extensions": {
"policies": [
{
"category": "view",
"implementation": PlotViewPolicy,
"depends": [
"openmct"
]
}
],
"views": [
{
"name": "Plot",
"key": "plot-single",
"cssClass": "icon-telemetry",
"template": PlotTemplate,
"needs": [
"telemetry"
],
"delegation": false,
"priority": "mandatory"
},
{
"name": "Overlay Plot",
"key": "overlayPlot",
"cssClass": "icon-plot-overlay",
"type": "telemetry.plot.overlay",
"template": PlotTemplate,
"editable": true
},
{
"name": "Stacked Plot",
"key": "stackedPlot",
"cssClass": "icon-plot-stacked",
"type": "telemetry.plot.stacked",
"template": StackedPlotTemplate,
"editable": true
}
],
"directives": [
{
"key": "mctTicks",
"implementation": MCTTicksDirective,
"depends": []
},
{
"key": "mctChart",
"implementation": MCTChartDirective,
"depends": [
"$interval",
"$log"
]
},
{
"key": "mctPlot",
"implementation": MCTPlotDirective,
"depends": [],
"templateUrl": "templates/mct-plot.html"
},
{
"key": "mctOverlayPlot",
"implementation": MCTOverlayPlot,
"depends": []
},
{
"key": "hideElementPool",
"implementation": HideElementPool,
"depends": []
}
],
"controllers": [
{
"key": "PlotController",
"implementation": PlotController,
"depends": [
"$scope",
"$element",
"formatService",
"openmct",
"objectService",
"exportImageService"
]
},
{
"key": "StackedPlotController",
"implementation": StackedPlotController,
"depends": [
"$scope",
"openmct",
"objectService",
"$element",
"exportImageService"
]
},
{
"key": "PlotOptionsController",
"implementation": PlotOptionsController,
"depends": [
"$scope",
"openmct",
"$timeout"
]
},
{
key: "PlotLegendFormController",
implementation: PlotLegendFormController,
depends: [
"$scope",
"openmct",
"$attrs"
]
},
{
key: "PlotYAxisFormController",
implementation: PlotYAxisFormController,
depends: [
"$scope",
"openmct",
"$attrs"
]
},
{
key: "PlotSeriesFormController",
implementation: PlotSeriesFormController,
depends: [
"$scope",
"openmct",
"$attrs"
]
}
],
"services": [
{
"key": "exportImageService",
"implementation": ExportImageService,
"depends": [
"dialogService"
]
}
],
"types": [
{
"key": "telemetry.plot.overlay",
"name": "Overlay Plot",
"cssClass": "icon-plot-overlay",
"description": "Combine multiple telemetry elements and view them together as a plot with common X and Y axes. Can be added to Display Layouts.",
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
composition: [],
configuration: {
series: [],
yAxis: {},
xAxis: {}
}
},
"properties": [],
"inspector": "plot-options",
"priority": 891
},
{
"key": "telemetry.plot.stacked",
"name": "Stacked Plot",
"cssClass": "icon-plot-stacked",
"description": "Combine multiple telemetry elements and view them together as a plot with a common X axis and individual Y axes. Can be added to Display Layouts.",
"features": "creation",
"contains": [
"telemetry.plot.overlay",
{"has": "telemetry"}
],
"model": {
"composition": [],
"configuration": {}
},
"properties": [],
"priority": 890
}
],
"representations": [
{
"key": "plot-options",
"template": plotOptionsTemplate
},
{
"key": "plot-options-browse",
"template": plotOptionsBrowseTemplate
},
{
"key": "plot-options-edit",
"template": plotOptionsEditTemplate
}
]
}
});
openmct.objectViews.addProvider(new StackedPlotViewProvider(openmct));
openmct.objectViews.addProvider(new OverlayPlotViewProvider(openmct));
openmct.objectViews.addProvider(new PlotViewProvider(openmct));
openmct.inspectorViews.addProvider(new PlotsInspectorViewProvider(openmct));
openmct.composition.addPolicy(new OverlayPlotCompositionPolicy(openmct).allow);
openmct.composition.addPolicy(new StackedPlotCompositionPolicy(openmct).allow);
};
}
openmct.legacyRegistry.enable("openmct/plot");
};
}
return PlotPlugin;
});

View File

@ -23,11 +23,11 @@
import {createMouseEvent, createOpenMct, resetApplicationState, spyOnBuiltins} from "utils/testing";
import PlotVuePlugin from "./plugin";
import Vue from "vue";
import StackedPlot from "../stackedPlot/StackedPlot.vue";
import configStore from "@/plugins/plot/vue/single/configuration/configStore";
import StackedPlot from "./stackedPlot/StackedPlot.vue";
import configStore from "./configuration/configStore";
import EventEmitter from "EventEmitter";
import PlotOptions from "../inspector/PlotOptions.vue";
import PlotConfigurationModel from "@/plugins/plot/vue/single/configuration/PlotConfigurationModel";
import PlotOptions from "./inspector/PlotOptions.vue";
import PlotConfigurationModel from "./configuration/PlotConfigurationModel";
describe("the plugin", function () {
let element;

View File

@ -1,279 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2021, 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.
-->
<div class="gl-plot plot-legend-{{legend.get('position')}} {{legend.get('expanded')? 'plot-legend-expanded' : 'plot-legend-collapsed'}}">
<div class="c-plot-legend gl-plot-legend"
ng-class="{
'hover-on-plot': !!highlights.length,
'is-legend-hidden': legend.get('hideLegendWhenSmall')
}"
>
<div class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled"
ng-class="{ 'c-disclosure-triangle--expanded': legend.get('expanded') }"
ng-click="legend.set('expanded', !legend.get('expanded'));">
</div>
<div class="c-plot-legend__wrapper"
ng-class="{ 'is-cursor-locked': !!lockHighlightPoint }">
<!-- COLLAPSED PLOT LEGEND -->
<div class="plot-wrapper-collapsed-legend"
ng-class="{'is-cursor-locked': !!lockHighlightPoint }">
<div class="c-state-indicator__alert-cursor-lock icon-cursor-lock" title="Cursor is point locked. Click anywhere in the plot to unlock."></div>
<div class="plot-legend-item"
ng-class="{
'is-status--missing': series.domainObject.status === 'missing'
}"
ng-repeat="series in series track by $index"
>
<div class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch"
ng-style="{ 'background-color': series.get('color').asHexString() }">
</span>
<span class="is-status__indicator" title="This item is missing or suspect"></span>
<span class="plot-series-name">{{ series.nameWithUnit() }}</span>
</div>
<div class="plot-series-value hover-value-enabled value-to-display-{{ legend.get('valueToShowWhenCollapsed') }} {{ series.closest.mctLimitState.cssClass }}"
ng-class="{ 'cursor-hover': (legend.get('valueToShowWhenCollapsed').indexOf('nearest') != -1) }"
ng-show="!!highlights.length && legend.get('valueToShowWhenCollapsed') !== 'none'">
{{ legend.get('valueToShowWhenCollapsed') === 'nearestValue' ?
series.formatY(series.closest) :
legend.get('valueToShowWhenCollapsed') === 'nearestTimestamp' ?
series.closest && series.formatX(series.closest) :
series.formatY(series.get('stats')[legend.get('valueToShowWhenCollapsed') + 'Point']);
}}
</div>
</div>
</div>
<!-- EXPANDED PLOT LEGEND -->
<div class="plot-wrapper-expanded-legend"
ng-class="{'is-cursor-locked': !!lockHighlightPoint }"
>
<div class="c-state-indicator__alert-cursor-lock--verbose icon-cursor-lock" title="Click anywhere in the plot to unlock."> Cursor locked to point</div>
<table>
<thead>
<tr>
<th>Name</th>
<th ng-if="legend.get('showTimestampWhenExpanded')">
Timestamp
</th>
<th ng-if="legend.get('showValueWhenExpanded')">
Value
</th>
<th ng-if="legend.get('showUnitsWhenExpanded')">
Unit
</th>
<th ng-if="legend.get('showMinimumWhenExpanded')"
class="mobile-hide">
Min
</th>
<th ng-if="legend.get('showMaximumWhenExpanded')"
class="mobile-hide">
Max
</th>
</tr>
</thead>
<tr ng-repeat="series in series"
class="plot-legend-item"
ng-class="{
'is-status--missing': series.domainObject.status === 'missing'
}"
>
<td class="plot-series-swatch-and-name">
<span class="plot-series-color-swatch"
ng-style="{ 'background-color': series.get('color').asHexString() }">
</span>
<span class="is-status__indicator" title="This item is missing or suspect"></span>
<span class="plot-series-name">{{ series.get('name') }}</span>
</td>
<td ng-if="legend.get('showTimestampWhenExpanded')">
<span class="plot-series-value cursor-hover hover-value-enabled">
{{ series.closest && series.formatX(series.closest) }}
</span>
</td>
<td ng-if="legend.get('showValueWhenExpanded')">
<span class="plot-series-value cursor-hover hover-value-enabled"
ng-class="series.closest.mctLimitState.cssClass">
{{ series.formatY(series.closest) }}
</span>
</td>
<td ng-if="legend.get('showUnitsWhenExpanded')">
<span class="plot-series-value cursor-hover hover-value-enabled">
{{ series.get('unit') }}
</span>
</td>
<td ng-if="legend.get('showMinimumWhenExpanded')"
class="mobile-hide">
<span class="plot-series-value">
{{ series.formatY(series.get('stats').minPoint) }}
</span>
</td>
<td ng-if="legend.get('showMaximumWhenExpanded')"
class="mobile-hide">
<span class="plot-series-value">
{{ series.formatY(series.get('stats').maxPoint) }}
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
<div class="gl-plot-axis-area gl-plot-y has-local-controls"
ng-style="{
width: (tickWidth + 20) + 'px'
}">
<div class="gl-plot-label gl-plot-y-label"
ng-class="{'icon-gear': (yKeyOptions.length > 1 && series.length === 1)}"
>{{yAxis.get('label')}}
</div>
<select class="gl-plot-y-label__select local-controls--hidden"
ng-if="yKeyOptions.length > 1 && series.length === 1"
ng-model="yAxisLabel" ng-change="plot.toggleYAxisLabel(yAxisLabel, yKeyOptions, series[0])">
<option ng-repeat="option in yKeyOptions"
value="{{option.name}}"
ng-selected="option.name === yAxisLabel">
{{option.name}}
</option>
</select>
<mct-ticks axis="yAxis">
<div ng-repeat="tick in ticks track by tick.value"
class="gl-plot-tick gl-plot-y-tick-label"
ng-style="{ top: (100 * (max - tick.value) / interval) + '%' }"
title="{{:: tick.fullText || tick.text }}"
style="margin-top: -0.50em; direction: ltr;">
<span>{{:: tick.text}}</span>
</div>
</mct-ticks>
</div>
<div class="gl-plot-wrapper-display-area-and-x-axis"
ng-style="{
left: (tickWidth + 20) + 'px'
}">
<div class="gl-plot-display-area has-local-controls has-cursor-guides">
<div class="l-state-indicators">
<span class="l-state-indicators__alert-no-lad t-object-alert t-alert-unsynced icon-alert-triangle"
title="This plot is not currently displaying the latest data. Reset pan/zoom to view latest data."></span>
</div>
<mct-ticks axis="xAxis">
<div class="gl-plot-hash hash-v"
ng-repeat="tick in ticks track by tick.value"
ng-style="{
right: (100 * (max - tick.value) / interval) + '%',
height: '100%'
}"
ng-show="plot.gridLines"
>
</div>
</mct-ticks>
<mct-ticks axis="yAxis">
<div class="gl-plot-hash hash-h"
ng-repeat="tick in ticks track by tick.value"
ng-style="{ bottom: (100 * (tick.value - min) / interval) + '%', width: '100%' }"
ng-show="plot.gridLines"
>
</div>
</mct-ticks>
<mct-chart config="config"
series="series"
rectangles="rectangles"
highlights="highlights"
the-x-axis="xAxis"
the-y-axis="yAxis">
</mct-chart>
<div class="gl-plot__local-controls h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover">
<div class="c-button-set c-button-set--strip-h">
<button class="c-button icon-minus"
ng-click="plot.zoom('out', 0.2)"
title="Zoom out">
</button>
<button class="c-button icon-plus"
ng-click="plot.zoom('in', 0.2)"
title="Zoom in">
</button>
</div>
<div class="c-button-set c-button-set--strip-h"
ng-disabled="!plotHistory.length">
<button class="c-button icon-arrow-left"
ng-click="plot.back()"
title="Restore previous pan/zoom">
</button>
<button class="c-button icon-reset"
ng-click="plot.clear()"
title="Reset pan/zoom">
</button>
</div>
</div>
<!--Cursor guides-->
<div class="c-cursor-guide--v js-cursor-guide--v"
ng-show="plot.cursorGuide">
</div>
<div class="c-cursor-guide--h js-cursor-guide--h"
ng-show="plot.cursorGuide">
</div>
</div>
<div class="gl-plot-axis-area gl-plot-x has-local-controls">
<mct-ticks axis="xAxis">
<div ng-repeat="tick in ticks track by tick.text"
class="gl-plot-tick gl-plot-x-tick-label"
ng-style="{
left: (100 * (tick.value - min) / interval) + '%'
}"
ng-title=":: tick.fullText || tick.text">
{{:: tick.text }}
</div>
</mct-ticks>
<div
class="gl-plot-label gl-plot-x-label"
ng-class="{'icon-gear': isEnabledXKeyToggle()}"
>
{{ xAxis.get('label') }}
</div>
<select
ng-show="plot.isEnabledXKeyToggle()"
ng-model="selectedXKeyOption.key"
ng-change="plot.toggleXKeyOption('{{selectedXKeyOption.key}}', series[0])"
class="gl-plot-x-label__select local-controls--hidden"
ng-options="option.key as option.name for option in xKeyOptions"
>
</select>
</div>
</div>
</div>
</div>

View File

@ -1,148 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2021, 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.
-->
<div ng-controller="PlotOptionsController">
<ul class="c-tree">
<h2 title="Plot series display properties in this object">Plot Series</h2>
<li ng-repeat="series in config.series.models">
<div class="c-tree__item menus-to-left">
<span class='c-disclosure-triangle is-enabled flex-elem'
ng-class="{ 'c-disclosure-triangle--expanded': series.expanded }"
ng-click="series.expanded = !series.expanded">
</span>
<mct-representation
class="rep-object-label c-tree__item__label"
key="'label'"
mct-object="series.oldObject">
</mct-representation>
</div>
<ul class="grid-properties" ng-show="series.expanded">
<li class="grid-row">
<div class="grid-cell label"
title="The field to be plotted as a value for this series.">Value</div>
<div class="grid-cell value">
{{series.get('yKey')}}
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="The rendering method to join lines for this series.">Line Method</div>
<div class="grid-cell value">{{ {
'none': 'None',
'linear': 'Linear interpolation',
'stepAfter': 'Step After'
}[series.get('interpolate')] }}
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Whether markers are displayed, and their size.">Markers</div>
<div class="grid-cell value">
{{ series.markerOptionsDisplayText() }}
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Display markers visually denoting points in alarm.">Alarm Markers</div>
<div class="grid-cell value">
{{series.get('alarmMarkers') ? "Enabled" : "Disabled"}}
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="The plot line and marker color for this series.">Color</div>
<div class="grid-cell value">
<span class="c-color-swatch"
ng-style="{
'background': series.get('color').asHexString()
}">
</span>
</div>
</li>
</ul>
</li><!-- end repeat -->
</ul>
<div class="grid-properties">
<ul class="l-inspector-part">
<h2 title="Y axis settings for this object">Y Axis</h2>
<li class="grid-row">
<div class="grid-cell label"
title="Manually override how the Y axis is labeled.">Label</div>
<div class="grid-cell value">{{ config.yAxis.get('label') ? config.yAxis.get('label') : "Not defined" }}</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Automatically scale the Y axis to keep all values in view.">Autoscale</div>
<div class="grid-cell value">
{{ config.yAxis.get('autoscale') ? "Enabled: " : "Disabled" }}
{{ config.yAxis.get('autoscale') ? (config.yAxis.get('autoscalePadding')) : ""}}
</div>
</li>
<li class="grid-row" ng-if="!form.yAxis.autoscale">
<div class="grid-cell label"
title="Minimum Y axis value.">Minimum value</div>
<div class="grid-cell value">{{ config.yAxis.get('range').min }}</div>
</li>
<li class="grid-row" ng-if="!form.yAxis.autoscale">
<div class="grid-cell label"
title="Maximum Y axis value.">Maximum value</div>
<div class="grid-cell value">{{ config.yAxis.get('range').max }}</div>
</li>
</ul>
<ul class="l-inspector-part">
<h2 title="Legend settings for this object">Legend</h2>
<li class="grid-row">
<div class="grid-cell label"
title="The position of the legend relative to the plot display area.">Position</div>
<div class="grid-cell value capitalize">{{ config.legend.get('position') }}</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Hide the legend when the plot is small">Hide when plot small</div>
<div class="grid-cell value">{{ config.legend.get('hideLegendWhenSmall') ? "Yes" : "No" }}</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Show the legend expanded by default">Expand by Default</div>
<div class="grid-cell value">{{ config.legend.get('expandByDefault') ? "Yes" : "No" }}</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="What to display in the legend when it's collapsed.">Show when collapsed:</div>
<div class="grid-cell value">{{
config.legend.get('valueToShowWhenCollapsed').replace('nearest', '')
}}
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="What to display in the legend when it's expanded.">Show when expanded:</div>
<div class="grid-cell value comma-list">
<span ng-if="config.legend.get('showTimestampWhenExpanded')">Timestamp</span>
<span ng-if="config.legend.get('showValueWhenExpanded')">Value</span>
<span ng-if="config.legend.get('showMinimumWhenExpanded')">Min</span>
<span ng-if="config.legend.get('showMaximumWhenExpanded')">Max</span>
<span ng-if="config.legend.get('showUnitsWhenExpanded')">Units</span>
</div>
</li>
</ul>
</div>
</div>

View File

@ -1,229 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2021, 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.
-->
<div ng-controller="PlotOptionsController">
<ul class="c-tree">
<h2 title="Display properties for this object">Plot Series</h2>
<li ng-repeat="series in plotSeries"
ng-controller="PlotSeriesFormController"
form-model="series">
<div class="c-tree__item menus-to-left">
<span class='c-disclosure-triangle is-enabled flex-elem'
ng-class="{ 'c-disclosure-triangle--expanded': expanded }"
ng-click="expanded = !expanded">
</span>
<mct-representation class="rep-object-label c-tree__item__label"
key="'label'"
mct-object="series.oldObject">
</mct-representation>
</div>
<ul class="grid-properties" ng-show="expanded">
<li class="grid-row">
<!-- Value to be displayed -->
<div class="grid-cell label"
title="The field to be plotted as a value for this series.">Value</div>
<div class="grid-cell value">
<select ng-model="form.yKey">
<option ng-repeat="option in yKeyOptions"
value="{{option.value}}"
ng-selected="option.value == form.yKey">
{{option.name}}
</option>
</select>
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="The rendering method to join lines for this series.">Line Method</div>
<div class="grid-cell value">
<select ng-model="form.interpolate">
<option value="none">None</option>
<option value="linear">Linear interpolate</option>
<option value="stepAfter">Step after</option>
</select>
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Whether markers are displayed.">Markers</div>
<div class="grid-cell value">
<input type="checkbox" ng-model="form.markers"/>
<select
ng-show="form.markers"
ng-model="form.markerShape">
<option
ng-repeat="option in markerShapeOptions"
value="{{ option.value }}"
ng-selected="option.value == form.markerShape"
>
{{ option.name }}
</option>
</select>
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Display markers visually denoting points in alarm.">Alarm Markers</div>
<div class="grid-cell value">
<input type="checkbox" ng-model="form.alarmMarkers"/>
</div>
</li>
<li class="grid-row" ng-show="form.markers || form.alarmMarkers">
<div class="grid-cell label"
title="The size of regular and alarm markers for this series.">Marker Size:</div>
<div class="grid-cell value"><input class="c-input--flex" type="text" ng-model="form.markerSize"/></div>
</li>
<li class="grid-row"
ng-controller="ClickAwayController as toggle"
ng-show="form.interpolate !== 'none' || form.markers">
<div class="grid-cell label"
title="Manually set the plot line and marker color for this series.">Color</div>
<div class="grid-cell value">
<div class="c-click-swatch c-click-swatch--menu" ng-click="toggle.toggle()">
<span class="c-color-swatch"
ng-style="{ background: series.get('color').asHexString() }">
</span>
</div>
<div class="c-palette c-palette--color">
<div class="c-palette__items" ng-show="toggle.isActive()">
<div class="u-contents" ng-repeat="group in config.series.palette.groups()">
<div class="c-palette__item"
ng-repeat="color in group"
ng-class="{ 'selected': series.get('color').equalTo(color) }"
ng-style="{ background: color.asHexString() }"
ng-click="setColor(color)">
</div>
</div>
</div>
</div>
</div>
</li>
</ul>
</li>
</ul>
<div class="grid-properties"
ng-show="!!config.series.models.length"
ng-controller="PlotYAxisFormController"
form-model="config.yAxis">
<ul class="l-inspector-part">
<h2>Y Axis</h2>
<li class="grid-row">
<div class="grid-cell label"
title="Manually override how the Y axis is labeled.">Label</div>
<div class="grid-cell value"><input class="c-input--flex" type="text" ng-model="form.label"/></div>
</li>
</ul>
<ul class="l-inspector-part">
<h2>Y Axis Scaling</h2>
<li class="grid-row">
<div class="grid-cell label"
title="Automatically scale the Y axis to keep all values in view.">Autoscale</div>
<div class="grid-cell value"><input type="checkbox" ng-model="form.autoscale"/></div>
</li>
<li class="grid-row" ng-show="form.autoscale">
<div class="grid-cell label"
title="Percentage of padding above and below plotted min and max values. 0.1, 1.0, etc.">
Padding</div>
<div class="grid-cell value">
<input class="c-input--flex" type="text" ng-model="form.autoscalePadding"/>
</div>
</li>
</ul>
<ul class="l-inspector-part" ng-show="!form.autoscale">
<div class="grid-span-all form-error"
ng-show="!form.autoscale && validation.range">
{{ validation.range }}
</div>
<li class="grid-row force-border">
<div class="grid-cell label"
title="Minimum Y axis value.">Minimum Value</div>
<div class="grid-cell value">
<input class="c-input--flex" type="number" ng-model="form.range.min"/>
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Maximum Y axis value.">Maximum Value</div>
<div class="grid-cell value"><input class="c-input--flex" type="number" ng-model="form.range.max"/></div>
</li>
</ul>
</div>
<div class="grid-properties" ng-show="!!config.series.models.length">
<ul class="l-inspector-part" ng-controller="PlotLegendFormController" form-model="config.legend">
<h2 title="Legend options">Legend</h2>
<li class="grid-row">
<div class="grid-cell label"
title="The position of the legend relative to the plot display area.">Position</div>
<div class="grid-cell value">
<select ng-model="form.position">
<option value="top">Top</option>
<option value="right">Right</option>
<option value="bottom">Bottom</option>
<option value="left">Left</option>
</select>
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Hide the legend when the plot is small">Hide when plot small</div>
<div class="grid-cell value"><input type="checkbox" ng-model="form.hideLegendWhenSmall"/></div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="Show the legend expanded by default">Expand by default</div>
<div class="grid-cell value"><input type="checkbox" ng-model="form.expandByDefault"/></div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="What to display in the legend when it's collapsed.">When collapsed show</div>
<div class="grid-cell value">
<select ng-model="form.valueToShowWhenCollapsed">
<option value="none">Nothing</option>
<option value="nearestTimestamp">Nearest timestamp</option>
<option value="nearestValue">Nearest value</option>
<option value="min">Minimum value</option>
<option value="max">Maximum value</option>
<option value="units">showUnitsWhenExpanded</option>
</select>
</div>
</li>
<li class="grid-row">
<div class="grid-cell label"
title="What to display in the legend when it's expanded.">When expanded show</div>
<div class="grid-cell value">
<ul>
<li><input type="checkbox"
ng-model="form.showTimestampWhenExpanded"/> Nearest timestamp</li>
<li><input type="checkbox"
ng-model="form.showValueWhenExpanded"/> Nearest value</li>
<li><input type="checkbox"
ng-model="form.showMinimumWhenExpanded"/> Minimum value</li>
<li><input type="checkbox"
ng-model="form.showMaximumWhenExpanded"/> Maximum value</li>
<li><input type="checkbox"
ng-model="form.showUnitsWhenExpanded"/> Units</li>
</ul>
</div>
</li>
</ul>
</div>
</div>

View File

@ -1,31 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2021, 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.
-->
<div ng-if="!domainObject.model.locked && domainObject.getCapability('editor').inEditContext()">
<mct-representation key="'plot-options-edit'"
mct-object="domainObject">
</mct-representation>
</div>
<div ng-if="domainObject.model.locked || !domainObject.getCapability('editor').inEditContext()">
<mct-representation key="'plot-options-browse'"
mct-object="domainObject">
</mct-representation>
</div>

View File

@ -1,58 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2021, 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.
-->
<div ng-controller="PlotController as controller"
class="c-plot holder holder-plot has-control-bar">
<div class="c-control-bar" ng-show="!controller.hideExportButtons">
<span class="c-button-set c-button-set--strip-h">
<button class="c-button icon-download"
ng-click="controller.exportPNG()"
title="Export This View's Data as PNG">
<span class="c-button__label">PNG</span>
</button>
<button class="c-button"
ng-click="controller.exportJPG()"
title="Export This View's Data as JPG">
<span class="c-button__label">JPG</span>
</button>
</span>
<button class="c-button icon-crosshair"
ng-class="{ 'is-active': controller.cursorGuide }"
ng-click="controller.toggleCursorGuide($event)"
title="Toggle cursor guides">
</button>
<button class="c-button"
ng-class="{ 'icon-grid-on': controller.gridLines, 'icon-grid-off': !controller.gridLines }"
ng-click="controller.toggleGridLines($event)"
title="Toggle grid lines">
</button>
</div>
<div class="l-view-section u-style-receiver js-style-receiver">
<div class="c-loading--overlay loading"
ng-show="!!pending"></div>
<mct-plot config="controller.config"
series="series"
the-y-axis="yAxis"
the-x-axis="xAxis">
</mct-plot>
</div>
</div>

View File

@ -1,65 +0,0 @@
<!--
Open MCT, Copyright (c) 2014-2021, 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.
-->
<div ng-controller="StackedPlotController as stackedPlot"
class="c-plot c-plot--stacked holder holder-plot has-control-bar">
<div class="c-control-bar" ng-show="!stackedPlot.hideExportButtons">
<span class="c-button-set c-button-set--strip-h">
<button class="c-button icon-download"
ng-click="stackedPlot.exportPNG()"
title="Export This View's Data as PNG">
<span class="c-button__label">PNG</span>
</button>
<button class="c-button"
ng-click="stackedPlot.exportJPG()"
title="Export This View's Data as JPG">
<span class="c-button__label">JPG</span>
</button>
</span>
<button class="c-button icon-crosshair"
ng-class="{ 'is-active': stackedPlot.cursorGuide }"
ng-click="stackedPlot.toggleCursorGuide($event)"
title="Toggle cursor guides">
</button>
<button class="c-button"
ng-class="{ 'icon-grid-on': stackedPlot.gridLines, 'icon-grid-off': !stackedPlot.gridLines }"
ng-click="stackedPlot.toggleGridLines($event)"
title="Toggle grid lines">
</button>
</div>
<div class="l-view-section u-style-receiver js-style-receiver">
<div class="c-loading--overlay loading"
ng-show="!!currentRequest.pending"></div>
<div class="gl-plot child-frame u-inspectable"
ng-repeat="telemetryObject in telemetryObjects"
ng-class="{
's-status-timeconductor-unsynced': telemetryObject
.getCapability('status')
.get('timeconductor-unsynced')
}"
mct-selectable="{
item: telemetryObject.useCapability('adapter'),
oldItem: telemetryObject
}">
<mct-overlay-plot domain-object="telemetryObject"></mct-overlay-plot>
</div>
</div>
</div>

View File

@ -1,68 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define(
function () {
/**
* Policy preventing the Plot view from being made available for
* domain objects which have non-numeric telemetry.
* @implements {Policy.<View, DomainObject>}
* @constructor
* @memberof platform/features/plot
*/
function PlotViewPolicy(openmct) {
this.openmct = openmct;
}
PlotViewPolicy.prototype.hasNumericTelemetry = function (domainObject) {
const adaptedObject = domainObject.useCapability('adapter');
if (!adaptedObject.telemetry) {
return domainObject.hasCapability('delegation')
&& domainObject.getCapability('delegation')
.doesDelegateCapability('telemetry');
}
const metadata = this.openmct.telemetry.getMetadata(adaptedObject);
const rangeValues = metadata.valuesForHints(['range']);
if (rangeValues.length === 0) {
return false;
}
return !rangeValues.every(function (value) {
return value.format === 'string';
});
};
PlotViewPolicy.prototype.allow = function (view, domainObject) {
if (view.key === 'plot-single') {
return this.hasNumericTelemetry(domainObject);
}
return true;
};
return PlotViewPolicy;
}
);

View File

@ -1,74 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'../lib/extend',
'../lib/eventHelpers'
], function (
extend,
eventHelpers
) {
function MCTChartAlarmPointSet(series, chart, offset) {
this.series = series;
this.chart = chart;
this.offset = offset;
this.points = [];
this.listenTo(series, 'add', this.append, this);
this.listenTo(series, 'remove', this.remove, this);
this.listenTo(series, 'reset', this.reset, this);
this.listenTo(series, 'destroy', this.destroy, this);
series.data.forEach(function (point, index) {
this.append(point, index, series);
}, this);
}
MCTChartAlarmPointSet.prototype.append = function (datum) {
if (datum.mctLimitState) {
this.points.push({
x: this.offset.xVal(datum, this.series),
y: this.offset.yVal(datum, this.series),
datum: datum
});
}
};
MCTChartAlarmPointSet.prototype.remove = function (datum) {
this.points = this.points.filter(function (p) {
return p.datum !== datum;
});
};
MCTChartAlarmPointSet.prototype.reset = function () {
this.points = [];
};
MCTChartAlarmPointSet.prototype.destroy = function () {
this.stopListening();
};
eventHelpers.extend(MCTChartAlarmPointSet.prototype);
return MCTChartAlarmPointSet;
});

View File

@ -1,441 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/**
* Module defining MCTChart. Created by vwoeltje on 11/12/14.
*/
define([
'./MCTChartLineLinear',
'./MCTChartLineStepAfter',
'./MCTChartPointSet',
'./MCTChartAlarmPointSet',
'../draw/DrawLoader',
'../lib/eventHelpers'
],
function (
MCTChartLineLinear,
MCTChartLineStepAfter,
MCTChartPointSet,
MCTChartAlarmPointSet,
DrawLoader,
eventHelpers
) {
const MARKER_SIZE = 6.0;
const HIGHLIGHT_SIZE = MARKER_SIZE * 2.0;
/**
* Offsetter adjusts x and y values by a fixed amount,
* generally increasing the precision of the 32 bit float representation
* required for plotting.
*
* @constructor
*/
function MCTChartController($scope) {
this.$onInit = () => {
this.$scope = $scope;
this.isDestroyed = false;
this.lines = [];
this.pointSets = [];
this.alarmSets = [];
this.offset = {};
this.config = $scope.config;
this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.draw = this.draw.bind(this);
this.scheduleDraw = this.scheduleDraw.bind(this);
this.seriesElements = new WeakMap();
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.yAxis, 'change', this.scheduleDraw);
this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
this.$scope.$watch('highlights', this.scheduleDraw);
this.$scope.$watch('rectangles', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this);
};
}
eventHelpers.extend(MCTChartController.prototype);
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);
this.listenTo(series, 'change', this.scheduleDraw);
this.listenTo(series, 'add', this.scheduleDraw);
this.makeChartElement(series);
};
MCTChartController.prototype.changeInterpolate = function (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);
}
};
MCTChartController.prototype.changeAlarmMarkers = function (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);
}
};
MCTChartController.prototype.changeMarkers = function (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);
}
};
MCTChartController.prototype.onSeriesRemove = function (series) {
this.stopListening(series);
this.removeChartElement(series);
this.scheduleDraw();
};
MCTChartController.prototype.destroy = function () {
this.isDestroyed = true;
this.stopListening();
this.lines.forEach(line => line.destroy());
DrawLoader.releaseDrawAPI(this.drawAPI);
};
MCTChartController.prototype.clearOffset = function () {
delete this.offset.x;
delete this.offset.y;
delete this.offset.xVal;
delete this.offset.yVal;
delete this.offset.xKey;
delete this.offset.yKey;
this.lines.forEach(function (line) {
line.reset();
});
this.pointSets.forEach(function (pointSet) {
pointSet.reset();
});
};
MCTChartController.prototype.setOffset = function (offsetPoint, index, series) {
if (this.offset.x && this.offset.y) {
return;
}
const offsets = {
x: series.getXVal(offsetPoint),
y: series.getYVal(offsetPoint)
};
this.offset.x = function (x) {
return x - offsets.x;
}.bind(this);
this.offset.y = function (y) {
return y - offsets.y;
}.bind(this);
this.offset.xVal = function (point, pSeries) {
return this.offset.x(pSeries.getXVal(point));
}.bind(this);
this.offset.yVal = function (point, pSeries) {
return this.offset.y(pSeries.getYVal(point));
}.bind(this);
};
MCTChartController.prototype.initializeCanvas = function (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);
};
MCTChartController.prototype.fallbackToCanvas = function () {
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.TEMPLATE;
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.$scope.$emit('plot:reinitializeCanvas');
};
MCTChartController.prototype.removeChartElement = function (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);
};
MCTChartController.prototype.lineForSeries = function (series) {
if (series.get('interpolate') === 'linear') {
return new MCTChartLineLinear(
series,
this,
this.offset
);
}
if (series.get('interpolate') === 'stepAfter') {
return new MCTChartLineStepAfter(
series,
this,
this.offset
);
}
};
MCTChartController.prototype.pointSetForSeries = function (series) {
if (series.get('markers')) {
return new MCTChartPointSet(
series,
this,
this.offset
);
}
};
MCTChartController.prototype.alarmPointSetForSeries = function (series) {
if (series.get('alarmMarkers')) {
return new MCTChartAlarmPointSet(
series,
this,
this.offset
);
}
};
MCTChartController.prototype.makeChartElement = function (series) {
const elements = {
lines: [],
pointSets: []
};
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);
};
MCTChartController.prototype.canDraw = function () {
if (!this.offset.x || !this.offset.y) {
return false;
}
return true;
};
MCTChartController.prototype.scheduleDraw = function () {
if (!this.drawScheduled) {
requestAnimationFrame(this.draw);
this.drawScheduled = true;
}
};
MCTChartController.prototype.draw = function () {
this.drawScheduled = false;
if (this.isDestroyed) {
return;
}
this.drawAPI.clear();
if (this.canDraw()) {
this.updateViewport();
this.drawSeries();
this.drawRectangles();
this.drawHighlights();
}
};
MCTChartController.prototype.updateViewport = function () {
const xRange = this.config.xAxis.get('displayRange');
const yRange = this.config.yAxis.get('displayRange');
if (!xRange || !yRange) {
return;
}
const dimensions = [
xRange.max - xRange.min,
yRange.max - yRange.min
];
const origin = [
this.offset.x(xRange.min),
this.offset.y(yRange.min)
];
this.drawAPI.setDimensions(
dimensions,
origin
);
};
MCTChartController.prototype.drawSeries = function () {
this.lines.forEach(this.drawLine, this);
this.pointSets.forEach(this.drawPoints, this);
this.alarmSets.forEach(this.drawAlarmPoints, this);
};
MCTChartController.prototype.drawAlarmPoints = function (alarmSet) {
this.drawAPI.drawLimitPoints(
alarmSet.points,
alarmSet.series.get('color').asRGBAArray(),
alarmSet.series.get('markerSize')
);
};
MCTChartController.prototype.drawPoints = function (chartElement) {
this.drawAPI.drawPoints(
chartElement.getBuffer(),
chartElement.color().asRGBAArray(),
chartElement.count,
chartElement.series.get('markerSize'),
chartElement.series.get('markerShape')
);
};
MCTChartController.prototype.drawLine = function (chartElement) {
this.drawAPI.drawLine(
chartElement.getBuffer(),
chartElement.color().asRGBAArray(),
chartElement.count
);
};
MCTChartController.prototype.drawHighlights = function () {
if (this.$scope.highlights && this.$scope.highlights.length) {
this.$scope.highlights.forEach(this.drawHighlight, this);
}
};
MCTChartController.prototype.drawHighlight = function (highlight) {
const points = new Float32Array([
this.offset.xVal(highlight.point, highlight.series),
this.offset.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);
};
MCTChartController.prototype.drawRectangles = function () {
if (this.$scope.rectangles) {
this.$scope.rectangles.forEach(this.drawRectangle, this);
}
};
MCTChartController.prototype.drawRectangle = function (rect) {
this.drawAPI.drawSquare(
[
this.offset.x(rect.start.x),
this.offset.y(rect.start.y)
],
[
this.offset.x(rect.end.x),
this.offset.y(rect.end.y)
],
rect.color
);
};
return MCTChartController;
});

View File

@ -1,67 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/**
* Module defining MCTChart. Created by vwoeltje on 11/12/14.
*/
define([
'./MCTChartController'
], function (
MCTChartController
) {
let TEMPLATE = "<canvas style='position: absolute; background: none; width: 100%; height: 100%;'></canvas>";
TEMPLATE += TEMPLATE;
/**
* MCTChart draws charts utilizing a drawAPI.
*
* @constructor
*/
function MCTChart() {
return {
restrict: "E",
template: TEMPLATE,
link: function ($scope, $element, attrs, ctrl) {
ctrl.TEMPLATE = TEMPLATE;
const mainCanvas = $element.find("canvas")[1];
const overlayCanvas = $element.find("canvas")[0];
if (ctrl.initializeCanvas(mainCanvas, overlayCanvas)) {
ctrl.draw();
}
},
controller: MCTChartController,
scope: {
config: "=",
draw: "=",
rectangles: "=",
series: "=",
xAxis: "=theXAxis",
yAxis: "=theYAxis",
highlights: "=?"
}
};
}
return MCTChart;
});

View File

@ -1,39 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./MCTChartSeriesElement'
], function (
MCTChartSeriesElement
) {
const MCTChartLineLinear = MCTChartSeriesElement.extend({
addPoint: function (point, start, count) {
this.buffer[start] = point.x;
this.buffer[start + 1] = point.y;
}
});
return MCTChartLineLinear;
});

View File

@ -1,79 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./MCTChartSeriesElement'
], function (
MCTChartSeriesElement
) {
const MCTChartLineStepAfter = MCTChartSeriesElement.extend({
removePoint: function (point, index, count) {
if (index > 0 && index / 2 < this.count) {
this.buffer[index + 1] = this.buffer[index - 1];
}
},
vertexCountForPointAtIndex: function (index) {
if (index === 0 && this.count === 0) {
return 2;
}
return 4;
},
startIndexForPointAtIndex: function (index) {
if (index === 0) {
return 0;
}
return 2 + ((index - 1) * 4);
},
addPoint: function (point, start, count) {
if (start === 0 && this.count === 0) {
// First point is easy.
this.buffer[start] = point.x;
this.buffer[start + 1] = point.y; // one point
} else if (start === 0 && this.count > 0) {
// Unshifting requires adding an extra point.
this.buffer[start] = point.x;
this.buffer[start + 1] = point.y;
this.buffer[start + 2] = this.buffer[start + 4];
this.buffer[start + 3] = point.y;
} else {
// Appending anywhere in line, insert standard two points.
this.buffer[start] = point.x;
this.buffer[start + 1] = this.buffer[start - 1];
this.buffer[start + 2] = point.x;
this.buffer[start + 3] = point.y;
if (start < this.count * 2) {
// Insert into the middle, need to update the following
// point.
this.buffer[start + 5] = point.y;
}
}
}
});
return MCTChartLineStepAfter;
});

View File

@ -1,39 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./MCTChartSeriesElement'
], function (
MCTChartSeriesElement
) {
const MCTChartPointSet = MCTChartSeriesElement.extend({
addPoint: function (point, start, count) {
this.buffer[start] = point.x;
this.buffer[start + 1] = point.y;
}
});
return MCTChartPointSet;
});

View File

@ -1,164 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'../lib/extend',
'../lib/eventHelpers'
], function (
extend,
eventHelpers
) {
function MCTChartSeriesElement(series, chart, offset) {
this.series = series;
this.chart = chart;
this.offset = offset;
this.buffer = new Float32Array(20000);
this.count = 0;
this.listenTo(series, 'add', this.append, this);
this.listenTo(series, 'remove', this.remove, this);
this.listenTo(series, 'reset', this.reset, this);
this.listenTo(series, 'destroy', this.destroy, this);
series.data.forEach(function (point, index) {
this.append(point, index, series);
}, this);
}
MCTChartSeriesElement.extend = extend;
eventHelpers.extend(MCTChartSeriesElement.prototype);
MCTChartSeriesElement.prototype.getBuffer = function () {
if (this.isTempBuffer) {
this.buffer = new Float32Array(this.buffer);
this.isTempBuffer = false;
}
return this.buffer;
};
MCTChartSeriesElement.prototype.color = function () {
return this.series.get('color');
};
MCTChartSeriesElement.prototype.vertexCountForPointAtIndex = function (index) {
return 2;
};
MCTChartSeriesElement.prototype.startIndexForPointAtIndex = function (index) {
return 2 * index;
};
MCTChartSeriesElement.prototype.removeSegments = function (index, count) {
const target = index;
const start = index + count;
const end = this.count * 2;
this.buffer.copyWithin(target, start, end);
for (let zero = end - count; zero < end; zero++) {
this.buffer[zero] = 0;
}
};
MCTChartSeriesElement.prototype.removePoint = function (point, index, count) {
// by default, do nothing.
};
MCTChartSeriesElement.prototype.remove = function (point, index, series) {
const vertexCount = this.vertexCountForPointAtIndex(index);
const removalPoint = this.startIndexForPointAtIndex(index);
this.removeSegments(removalPoint, vertexCount);
this.removePoint(
this.makePoint(point, series),
removalPoint,
vertexCount
);
this.count -= (vertexCount / 2);
};
MCTChartSeriesElement.prototype.makePoint = function (point, series) {
if (!this.offset.xVal) {
this.chart.setOffset(point, undefined, series);
}
return {
x: this.offset.xVal(point, series),
y: this.offset.yVal(point, series)
};
};
MCTChartSeriesElement.prototype.append = function (point, index, series) {
const pointsRequired = this.vertexCountForPointAtIndex(index);
const insertionPoint = this.startIndexForPointAtIndex(index);
this.growIfNeeded(pointsRequired);
this.makeInsertionPoint(insertionPoint, pointsRequired);
this.addPoint(
this.makePoint(point, series),
insertionPoint,
pointsRequired
);
this.count += (pointsRequired / 2);
};
MCTChartSeriesElement.prototype.makeInsertionPoint = function (insertionPoint, pointsRequired) {
if (this.count * 2 > insertionPoint) {
if (!this.isTempBuffer) {
this.buffer = Array.prototype.slice.apply(this.buffer);
this.isTempBuffer = true;
}
const target = insertionPoint + pointsRequired;
let start = insertionPoint;
for (; start < target; start++) {
this.buffer.splice(start, 0, 0);
}
}
};
MCTChartSeriesElement.prototype.reset = function () {
this.buffer = new Float32Array(20000);
this.count = 0;
if (this.offset.x) {
this.series.data.forEach(function (point, index) {
this.append(point, index, this.series);
}, this);
}
};
MCTChartSeriesElement.prototype.growIfNeeded = function (pointsRequired) {
const remainingPoints = this.buffer.length - this.count * 2;
let temp;
if (remainingPoints <= pointsRequired) {
temp = new Float32Array(this.buffer.length + 20000);
temp.set(this.buffer);
this.buffer = temp;
}
};
MCTChartSeriesElement.prototype.destroy = function () {
this.stopListening();
};
return MCTChartSeriesElement;
});

View File

@ -1,130 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'EventEmitter',
'./Model',
'../lib/extend',
'../lib/eventHelpers'
], function (
EventEmitter,
Model,
extend,
eventHelpers
) {
function Collection(options) {
if (options.models) {
this.models = options.models.map(this.modelFn, this);
} else {
this.models = [];
}
this.initialize(options);
}
Object.assign(Collection.prototype, EventEmitter.prototype);
eventHelpers.extend(Collection.prototype);
Collection.extend = extend;
Collection.prototype.initialize = function (options) {
};
Collection.prototype.modelClass = Model;
Collection.prototype.modelFn = function (model) {
if (model instanceof this.modelClass) {
model.collection = this;
return model;
}
return new this.modelClass({
collection: this,
model: model
});
};
Collection.prototype.first = function () {
return this.at(0);
};
Collection.prototype.forEach = function (iteree, context) {
this.models.forEach(iteree, context);
};
Collection.prototype.map = function (iteree, context) {
return this.models.map(iteree, context);
};
Collection.prototype.filter = function (iteree, context) {
return this.models.filter(iteree, context);
};
Collection.prototype.size = function () {
return this.models.length;
};
Collection.prototype.at = function (index) {
return this.models[index];
};
Collection.prototype.add = function (model) {
model = this.modelFn(model);
const index = this.models.length;
this.models.push(model);
this.emit('add', model, index);
};
Collection.prototype.insert = function (model, index) {
model = this.modelFn(model);
this.models.splice(index, 0, model);
this.emit('add', model, index + 1);
};
Collection.prototype.indexOf = function (model) {
return this.models.findIndex(m => m === model);
};
Collection.prototype.remove = function (model) {
const index = this.indexOf(model);
if (index === -1) {
throw new Error('model not found in collection.');
}
this.emit('remove', model, index);
this.models.splice(index, 1);
};
Collection.prototype.destroy = function (model) {
this.forEach(function (m) {
m.destroy();
});
this.stopListening();
};
return Collection;
});

View File

@ -1,63 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./Model'
], function (
Model
) {
/**
* TODO: doc strings.
*/
const LegendModel = Model.extend({
listenToSeriesCollection: function (seriesCollection) {
this.seriesCollection = seriesCollection;
this.listenTo(this.seriesCollection, 'add', this.setHeight, this);
this.listenTo(this.seriesCollection, 'remove', this.setHeight, this);
this.listenTo(this, 'change:expanded', this.setHeight, this);
this.set('expanded', this.get('expandByDefault'));
},
setHeight: function () {
const expanded = this.get('expanded');
if (this.get('position') !== 'top') {
this.set('height', '0px');
} else {
this.set('height', expanded ? (20 * (this.seriesCollection.size() + 1) + 40) + 'px' : '21px');
}
},
defaults: function (options) {
return {
position: 'top',
expandByDefault: false,
hideLegendWhenSmall: false,
valueToShowWhenCollapsed: 'nearestValue',
showTimestampWhenExpanded: true,
showValueWhenExpanded: true,
showMaximumWhenExpanded: true,
showMinimumWhenExpanded: true,
showUnitsWhenExpanded: true
};
}
});
return LegendModel;
});

View File

@ -1,103 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'lodash',
'EventEmitter',
'../lib/extend',
'../lib/eventHelpers'
], function (
_,
EventEmitter,
extend,
eventHelpers
) {
function Model(options) {
if (!options) {
options = {};
}
this.id = options.id;
this.model = options.model;
this.collection = options.collection;
const defaults = this.defaults(options);
if (!this.model) {
this.model = options.model = defaults;
} else {
_.defaultsDeep(this.model, defaults);
}
this.initialize(options);
}
Object.assign(Model.prototype, EventEmitter.prototype);
eventHelpers.extend(Model.prototype);
Model.extend = extend;
Model.prototype.idAttr = 'id';
Model.prototype.defaults = function (options) {
return {};
};
Model.prototype.initialize = function (model) {
};
/**
* Destroy the model, removing all listeners and subscriptions.
*/
Model.prototype.destroy = function () {
this.emit('destroy');
this.removeAllListeners();
};
Model.prototype.id = function () {
return this.get(this.idAttr);
};
Model.prototype.get = function (attribute) {
return this.model[attribute];
};
Model.prototype.has = function (attribute) {
return _.has(this.model, attribute);
};
Model.prototype.set = function (attribute, value) {
const oldValue = this.model[attribute];
this.model[attribute] = value;
this.emit('change', attribute, value, oldValue, this);
this.emit('change:' + attribute, value, oldValue, this);
};
Model.prototype.unset = function (attribute) {
const oldValue = this.model[attribute];
delete this.model[attribute];
this.emit('change', attribute, undefined, oldValue, this);
this.emit('change:' + attribute, undefined, oldValue, this);
};
return Model;
});

View File

@ -1,152 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./Collection',
'./Model',
'./SeriesCollection',
'./XAxisModel',
'./YAxisModel',
'./LegendModel',
'lodash'
], function (
Collection,
Model,
SeriesCollection,
XAxisModel,
YAxisModel,
LegendModel,
_
) {
/**
* PlotConfiguration model stores the configuration of a plot and some
* limited state. The indiidual parts of the plot configuration model
* handle setting defaults and updating in response to various changes.
*
*/
const PlotConfigurationModel = Model.extend({
/**
* Initializes all sub models and then passes references to submodels
* to those that need it.
*/
initialize: function (options) {
this.openmct = options.openmct;
this.xAxis = new XAxisModel({
model: options.model.xAxis,
plot: this,
openmct: options.openmct
});
this.yAxis = new YAxisModel({
model: options.model.yAxis,
plot: this,
openmct: options.openmct
});
this.legend = new LegendModel({
model: options.model.legend,
plot: this,
openmct: options.openmct
});
this.series = new SeriesCollection({
models: options.model.series,
plot: this,
openmct: options.openmct
});
if (this.get('domainObject').type === 'telemetry.plot.overlay') {
this.removeMutationListener = this.openmct.objects.observe(
this.get('domainObject'),
'*',
this.updateDomainObject.bind(this)
);
}
this.yAxis.listenToSeriesCollection(this.series);
this.legend.listenToSeriesCollection(this.series);
this.listenTo(this, 'destroy', this.onDestroy, this);
},
/**
* Retrieve the persisted series config for a given identifier.
*/
getPersistedSeriesConfig: function (identifier) {
const domainObject = this.get('domainObject');
if (!domainObject.configuration || !domainObject.configuration.series) {
return;
}
return domainObject.configuration.series.filter(function (seriesConfig) {
return seriesConfig.identifier.key === identifier.key
&& seriesConfig.identifier.namespace === identifier.namespace;
})[0];
},
/**
* Retrieve the persisted filters for a given identifier.
*/
getPersistedFilters: function (identifier) {
const domainObject = this.get('domainObject');
const keystring = this.openmct.objects.makeKeyString(identifier);
if (!domainObject.configuration || !domainObject.configuration.filters) {
return;
}
return domainObject.configuration.filters[keystring];
},
/**
* Update the domain object with the given value.
*/
updateDomainObject: function (domainObject) {
this.set('domainObject', domainObject);
},
/**
* Clean up all objects and remove all listeners.
*/
onDestroy: function () {
this.xAxis.destroy();
this.yAxis.destroy();
this.series.destroy();
this.legend.destroy();
if (this.removeMutationListener) {
this.removeMutationListener();
}
},
/**
* Return defaults, which are extracted from the passed in domain
* object.
*/
defaults: function (options) {
return {
series: [],
domainObject: options.domainObject,
xAxis: {
},
yAxis: _.cloneDeep(_.get(options.domainObject, 'configuration.yAxis', {})),
legend: _.cloneDeep(_.get(options.domainObject, 'configuration.legend', {}))
};
}
});
return PlotConfigurationModel;
});

View File

@ -1,469 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'lodash',
'../configuration/Model',
'../lib/extend',
'EventEmitter',
'../draw/MarkerShapes'
], function (
_,
Model,
extend,
EventEmitter,
MARKER_SHAPES
) {
/**
* Plot series handle interpreting telemetry metadata for a single telemetry
* object, querying for that data, and formatting it for display purposes.
*
* Plot series emit both collection events and model events:
* `change` when any property changes
* `change:<prop_name>` when a specific property changes.
* `destroy`: when series is destroyed
* `add`: whenever a data point is added to a series
* `remove`: whenever a data point is removed from a series.
* `reset`: whenever the collection is emptied.
*
* Plot series have the following Model properties:
*
* `name`: name of series.
* `identifier`: the Open MCT identifier for the telemetry source for this
* series.
* `xKey`: the telemetry value key for x values fetched from this series.
* `yKey`: the telemetry value key for y values fetched from this series.
* `interpolate`: interpolate method, either `undefined` (no interpolation),
* `linear` (points are connected via straight lines), or
* `stepAfter` (points are connected by steps).
* `markers`: boolean, whether or not this series should render with markers.
* `markerShape`: string, shape of markers.
* `markerSize`: number, size in pixels of markers for this series.
* `alarmMarkers`: whether or not to display alarm markers for this series.
* `stats`: An object that tracks the min and max y values observed in this
* series. This property is checked and updated whenever data is
* added.
*
* Plot series have the following instance properties:
*
* `metadata`: the Open MCT Telemetry Metadata Manager for the associated
* telemetry point.
* `formats`: the Open MCT format map for this telemetry point.
*/
const PlotSeries = Model.extend({
constructor: function (options) {
this.metadata = options
.openmct
.telemetry
.getMetadata(options.domainObject);
this.formats = options
.openmct
.telemetry
.getFormatMap(this.metadata);
this.data = [];
this.listenTo(this, 'change:xKey', this.onXKeyChange, this);
this.listenTo(this, 'change:yKey', this.onYKeyChange, this);
this.persistedConfig = options.persistedConfig;
this.filters = options.filters;
Model.apply(this, arguments);
this.onXKeyChange(this.get('xKey'));
this.onYKeyChange(this.get('yKey'));
},
/**
* Set defaults for telemetry series.
*/
defaults: function (options) {
const range = this.metadata.valuesForHints(['range'])[0];
return {
name: options.domainObject.name,
unit: range.unit,
xKey: options.collection.plot.xAxis.get('key'),
yKey: range.key,
markers: true,
markerShape: 'point',
markerSize: 2.0,
alarmMarkers: true
};
},
/**
* Remove real-time subscription when destroyed.
*/
onDestroy: function (model) {
if (this.unsubscribe) {
this.unsubscribe();
}
},
initialize: function (options) {
this.openmct = options.openmct;
this.domainObject = options.domainObject;
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(options.domainObject);
this.on('destroy', this.onDestroy, this);
},
locateOldObject: function (oldStyleParent) {
return oldStyleParent.useCapability('composition')
.then(function (children) {
this.oldObject = children
.filter(function (child) {
return child.getId() === this.keyString;
}, this)[0];
}.bind(this));
},
/**
* Fetch historical data and establish a realtime subscription. Returns
* a promise that is resolved when all connections have been successfully
* established.
*
* @returns {Promise}
*/
fetch: function (options) {
let strategy;
if (this.model.interpolate !== 'none') {
strategy = 'minmax';
}
options = Object.assign({}, {
size: 1000,
strategy,
filters: this.filters
}, options || {});
if (!this.unsubscribe) {
this.unsubscribe = this.openmct
.telemetry
.subscribe(
this.domainObject,
this.add.bind(this),
{
filters: this.filters
}
);
}
/* eslint-disable you-dont-need-lodash-underscore/concat */
return this.openmct
.telemetry
.request(this.domainObject, options)
.then(function (points) {
const newPoints = _(this.data)
.concat(points)
.sortBy(this.getXVal)
.uniq(true, point => [this.getXVal(point), this.getYVal(point)].join())
.value();
this.reset(newPoints);
}.bind(this));
/* eslint-enable you-dont-need-lodash-underscore/concat */
},
/**
* Update x formatter on x change.
*/
onXKeyChange: function (xKey) {
const format = this.formats[xKey];
this.getXVal = format.parse.bind(format);
},
/**
* Update y formatter on change, default to stepAfter interpolation if
* y range is an enumeration.
*/
onYKeyChange: function (newKey, oldKey) {
if (newKey === oldKey) {
return;
}
const valueMetadata = this.metadata.value(newKey);
if (!this.persistedConfig || !this.persistedConfig.interpolate) {
if (valueMetadata.format === 'enum') {
this.set('interpolate', 'stepAfter');
} else {
this.set('interpolate', 'linear');
}
}
this.evaluate = function (datum) {
return this.limitEvaluator.evaluate(datum, valueMetadata);
}.bind(this);
const format = this.formats[newKey];
this.getYVal = format.parse.bind(format);
},
formatX: function (point) {
return this.formats[this.get('xKey')].format(point);
},
formatY: function (point) {
return this.formats[this.get('yKey')].format(point);
},
/**
* Clear stats and recalculate from existing data.
*/
resetStats: function () {
this.unset('stats');
this.data.forEach(this.updateStats, this);
},
/**
* Reset plot series. If new data is provided, will add that
* data to series after reset.
*/
reset: function (newData) {
this.data = [];
this.resetStats();
this.emit('reset');
if (newData) {
newData.forEach(function (point) {
this.add(point, true);
}, this);
}
},
/**
* Return the point closest to a given x value.
*/
nearestPoint: function (xValue) {
const insertIndex = this.sortedIndex(xValue);
const lowPoint = this.data[insertIndex - 1];
const highPoint = this.data[insertIndex];
const indexVal = this.getXVal(xValue);
const lowDistance = lowPoint
? indexVal - this.getXVal(lowPoint)
: Number.POSITIVE_INFINITY;
const highDistance = highPoint
? this.getXVal(highPoint) - indexVal
: Number.POSITIVE_INFINITY;
const nearestPoint = highDistance < lowDistance ? highPoint : lowPoint;
return nearestPoint;
},
/**
* Override this to implement plot series loading functionality. Must return
* a promise that is resolved when loading is completed.
*
* @private
* @returns {Promise}
*/
load: function (options) {
return this.fetch(options)
.then(function (res) {
this.emit('load');
return res;
}.bind(this));
},
/**
* Find the insert index for a given point to maintain sort order.
* @private
*/
sortedIndex: function (point) {
return _.sortedIndexBy(this.data, point, this.getXVal);
},
/**
* Update min/max stats for the series.
* @private
*/
updateStats: function (point) {
const value = this.getYVal(point);
let stats = this.get('stats');
let changed = false;
if (!stats) {
stats = {
minValue: value,
minPoint: point,
maxValue: value,
maxPoint: point
};
changed = true;
} else {
if (stats.maxValue < value) {
stats.maxValue = value;
stats.maxPoint = point;
changed = true;
}
if (stats.minValue > value) {
stats.minValue = value;
stats.minPoint = point;
changed = true;
}
}
if (changed) {
this.set('stats', {
minValue: stats.minValue,
minPoint: stats.minPoint,
maxValue: stats.maxValue,
maxPoint: stats.maxPoint
});
}
},
/**
* Add a point to the data array while maintaining the sort order of
* the array and preventing insertion of points with a duplicate x
* value. Can provide an optional argument to append a point without
* maintaining sort order and dupe checks, which improves performance
* when adding an array of points that are already properly sorted.
*
* @private
* @param {Object} point a telemetry datum.
* @param {Boolean} [appendOnly] default false, if true will append
* a point to the end without dupe checking.
*/
add: function (point, appendOnly) {
let insertIndex = this.data.length;
const currentYVal = this.getYVal(point);
const lastYVal = this.getYVal(this.data[insertIndex - 1]);
if (this.isValueInvalid(currentYVal) && this.isValueInvalid(lastYVal)) {
console.warn('[Plot] Invalid Y Values detected');
return;
}
if (!appendOnly) {
insertIndex = this.sortedIndex(point);
if (this.getXVal(this.data[insertIndex]) === this.getXVal(point)) {
return;
}
if (this.getXVal(this.data[insertIndex - 1]) === this.getXVal(point)) {
return;
}
}
this.updateStats(point);
point.mctLimitState = this.evaluate(point);
this.data.splice(insertIndex, 0, point);
this.emit('add', point, insertIndex, this);
},
/**
*
* @private
*/
isValueInvalid: function (val) {
return Number.isNaN(val) || val === undefined;
},
/**
* Remove a point from the data array and notify listeners.
* @private
*/
remove: function (point) {
const index = this.data.indexOf(point);
this.data.splice(index, 1);
this.emit('remove', point, index, this);
},
/**
* Purges records outside a given x range. Changes removal method based
* on number of records to remove: for large purge, reset data and
* rebuild array. for small purge, removes points and emits updates.
*
* @public
* @param {Object} range
* @param {number} range.min minimum x value to keep
* @param {number} range.max maximum x value to keep.
*/
purgeRecordsOutsideRange: function (range) {
const startIndex = this.sortedIndex(range.min);
const endIndex = this.sortedIndex(range.max) + 1;
const pointsToRemove = startIndex + (this.data.length - endIndex + 1);
if (pointsToRemove > 0) {
if (pointsToRemove < 1000) {
this.data.slice(0, startIndex).forEach(this.remove, this);
this.data.slice(endIndex, this.data.length).forEach(this.remove, this);
this.resetStats();
} else {
const newData = this.data.slice(startIndex, endIndex);
this.reset(newData);
}
}
},
/**
* Updates filters, clears the plot series, unsubscribes and resubscribes
* @public
*/
updateFiltersAndRefresh: function (updatedFilters) {
if (updatedFilters === undefined) {
return;
}
let deepCopiedFilters = JSON.parse(JSON.stringify(updatedFilters));
if (this.filters && !_.isEqual(this.filters, deepCopiedFilters)) {
this.filters = deepCopiedFilters;
this.reset();
if (this.unsubscribe) {
this.unsubscribe();
delete this.unsubscribe;
}
this.fetch();
} else {
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) {
return "Disabled";
}
const markerShapeKey = this.get('markerShape');
const markerShape = MARKER_SHAPES[markerShapeKey].label;
const markerSize = this.get('markerSize');
return `${markerShape}: ${markerSize}px`;
},
nameWithUnit: function () {
let unit = this.get('unit');
return this.get('name') + (unit ? ' ' + unit : '');
}
});
return PlotSeries;
});

View File

@ -1,174 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./PlotSeries',
'./Collection',
'./Model',
'../lib/color',
'lodash'
], function (
PlotSeries,
Collection,
Model,
color,
_
) {
const SeriesCollection = Collection.extend({
modelClass: PlotSeries,
initialize: function (options) {
this.plot = options.plot;
this.openmct = options.openmct;
this.palette = new color.ColorPalette();
this.listenTo(this, 'add', this.onSeriesAdd, this);
this.listenTo(this, 'remove', this.onSeriesRemove, this);
this.listenTo(this.plot, 'change:domainObject', this.trackPersistedConfig, this);
const domainObject = this.plot.get('domainObject');
if (domainObject.telemetry) {
this.addTelemetryObject(domainObject);
} else {
this.watchTelemetryContainer(domainObject);
}
},
trackPersistedConfig: function (domainObject) {
domainObject.configuration.series.forEach(function (seriesConfig) {
const series = this.byIdentifier(seriesConfig.identifier);
if (series) {
series.persistedConfig = seriesConfig;
}
}, this);
},
watchTelemetryContainer: function (domainObject) {
const composition = this.openmct.composition.get(domainObject);
this.listenTo(composition, 'add', this.addTelemetryObject, this);
this.listenTo(composition, 'remove', this.removeTelemetryObject, this);
composition.load();
},
addTelemetryObject: function (domainObject, index) {
let seriesConfig = this.plot.getPersistedSeriesConfig(domainObject.identifier);
const filters = this.plot.getPersistedFilters(domainObject.identifier);
const plotObject = this.plot.get('domainObject');
if (!seriesConfig) {
seriesConfig = {
identifier: domainObject.identifier
};
if (plotObject.type === 'telemetry.plot.overlay') {
this.openmct.objects.mutate(
plotObject,
'configuration.series[' + this.size() + ']',
seriesConfig
);
seriesConfig = this.plot
.getPersistedSeriesConfig(domainObject.identifier);
}
}
// Clone to prevent accidental mutation by ref.
seriesConfig = JSON.parse(JSON.stringify(seriesConfig));
this.add(new PlotSeries({
model: seriesConfig,
domainObject: domainObject,
collection: this,
openmct: this.openmct,
persistedConfig: this.plot
.getPersistedSeriesConfig(domainObject.identifier),
filters: filters
}));
},
removeTelemetryObject: function (identifier) {
const plotObject = this.plot.get('domainObject');
if (plotObject.type === 'telemetry.plot.overlay') {
const persistedIndex = plotObject.configuration.series.findIndex(s => {
return _.isEqual(identifier, s.identifier);
});
const configIndex = this.models.findIndex(m => {
return _.isEqual(m.domainObject.identifier, identifier);
});
/*
when cancelling out of edit mode, the config store and domain object are out of sync
thus it is necesarry to check both and remove the models that are no longer in composition
*/
if (persistedIndex === -1) {
this.remove(this.at(configIndex));
} else {
this.remove(this.at(persistedIndex));
// Because this is triggered by a composition change, we have
// to defer mutation of our plot object, otherwise we might
// mutate an outdated version of the plotObject.
setTimeout(function () {
const newPlotObject = this.plot.get('domainObject');
const cSeries = newPlotObject.configuration.series.slice();
cSeries.splice(persistedIndex, 1);
this.openmct.objects.mutate(newPlotObject, 'configuration.series', cSeries);
}.bind(this));
}
}
},
onSeriesAdd: function (series) {
let seriesColor = series.get('color');
if (seriesColor) {
if (!(seriesColor instanceof color.Color)) {
seriesColor = color.Color.fromHexString(seriesColor);
series.set('color', seriesColor);
}
this.palette.remove(seriesColor);
} else {
series.set('color', this.palette.getNextColor());
}
this.listenTo(series, 'change:color', this.updateColorPalette, this);
},
onSeriesRemove: function (series) {
this.palette.return(series.get('color'));
this.stopListening(series);
series.destroy();
},
updateColorPalette: function (newColor, oldColor) {
this.palette.remove(newColor);
const seriesWithColor = this.filter(function (series) {
return series.get('color') === newColor;
})[0];
if (!seriesWithColor) {
this.palette.return(oldColor);
}
},
byIdentifier: function (identifier) {
return this.filter(function (series) {
const seriesIdentifier = series.get('identifier');
return seriesIdentifier.namespace === identifier.namespace
&& seriesIdentifier.key === identifier.key;
})[0];
}
});
return SeriesCollection;
});

View File

@ -1,97 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./Model'
], function (
Model
) {
/**
* TODO: doc strings.
*/
const XAxisModel = Model.extend({
initialize: function (options) {
this.plot = options.plot;
this.set('label', options.model.name || '');
this.on('change:range', function (newValue, oldValue, model) {
if (!model.get('frozen')) {
model.set('displayRange', newValue);
}
});
this.on('change:frozen', ((frozen, oldValue, model) => {
if (!frozen) {
model.set('range', this.get('range'));
}
}));
if (this.get('range')) {
this.set('range', this.get('range'));
}
this.listenTo(this, 'change:key', this.changeKey, this);
this.listenTo(this, 'resetSeries', this.resetSeries, this);
},
changeKey: function (newKey) {
const series = this.plot.series.first();
if (series) {
const xMetadata = series.metadata.value(newKey);
const xFormat = series.formats[newKey];
this.set('label', xMetadata.name);
this.set('format', xFormat.format.bind(xFormat));
} else {
this.set('format', function (x) {
return x;
});
this.set('label', newKey);
}
this.plot.series.forEach(function (plotSeries) {
plotSeries.set('xKey', newKey);
});
},
resetSeries: function () {
this.plot.series.forEach(function (plotSeries) {
plotSeries.reset();
});
},
defaults: function (options) {
const bounds = options.openmct.time.bounds();
const timeSystem = options.openmct.time.timeSystem();
const format = options.openmct.$injector.get('formatService')
.getFormat(timeSystem.timeFormat);
return {
name: timeSystem.name,
key: timeSystem.key,
format: format.format.bind(format),
range: {
min: bounds.start,
max: bounds.end
},
frozen: false
};
}
});
return XAxisModel;
});

View File

@ -1,244 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./Model',
'lodash'
], function (
Model,
_
) {
/**
* YAxis model
*
* TODO: docstrings.
*
* has the following Model properties:
*
* `autoscale`: boolean, whether or not to autoscale.
* `autoscalePadding`: float, percent of padding to display in plots.
* `displayRange`: the current display range for the x Axis.
* `format`: the formatter for the axis.
* `frozen`: boolean, if true, displayRange will not be updated automatically.
* Used to temporarily disable automatic updates during user interaction.
* `label`: label to display on axis.
* `stats`: Min and Max Values of data, automatically updated by observing
* plot series.
* `values`: for enumerated types, an array of possible display values.
* `range`: the user-configured range to use for display, when autoscale is
* disabled.
*
*/
const YAxisModel = Model.extend({
initialize: function (options) {
this.plot = options.plot;
this.listenTo(this, 'change:stats', this.calculateAutoscaleExtents, this);
this.listenTo(this, 'change:autoscale', this.toggleAutoscale, this);
this.listenTo(this, 'change:autoscalePadding', this.updatePadding, this);
this.listenTo(this, 'change:frozen', this.toggleFreeze, this);
this.listenTo(this, 'change:range', this.updateDisplayRange, this);
this.updateDisplayRange(this.get('range'));
},
listenToSeriesCollection: function (seriesCollection) {
this.seriesCollection = seriesCollection;
this.listenTo(this.seriesCollection, 'add', (series => {
this.trackSeries(series);
this.updateFromSeries(this.seriesCollection);
}), this);
this.listenTo(this.seriesCollection, 'remove', (series => {
this.untrackSeries(series);
this.updateFromSeries(this.seriesCollection);
}), this);
this.seriesCollection.forEach(this.trackSeries, this);
this.updateFromSeries(this.seriesCollection);
},
updateDisplayRange: function (range) {
if (!this.get('autoscale')) {
this.set('displayRange', range);
}
},
toggleFreeze: function (frozen) {
if (!frozen) {
this.toggleAutoscale(this.get('autoscale'));
}
},
applyPadding: function (range) {
let padding = Math.abs(range.max - range.min) * this.get('autoscalePadding');
if (padding === 0) {
padding = 1;
}
return {
min: range.min - padding,
max: range.max + padding
};
},
updatePadding: function (newPadding) {
if (this.get('autoscale') && !this.get('frozen') && this.has('stats')) {
this.set('displayRange', this.applyPadding(this.get('stats')));
}
},
calculateAutoscaleExtents: function (newStats) {
if (this.get('autoscale') && !this.get('frozen')) {
if (!newStats) {
this.unset('displayRange');
} else {
this.set('displayRange', this.applyPadding(newStats));
}
}
},
updateStats: function (seriesStats) {
if (!this.has('stats')) {
this.set('stats', {
min: seriesStats.minValue,
max: seriesStats.maxValue
});
return;
}
const stats = this.get('stats');
let changed = false;
if (stats.min > seriesStats.minValue) {
changed = true;
stats.min = seriesStats.minValue;
}
if (stats.max < seriesStats.maxValue) {
changed = true;
stats.max = seriesStats.maxValue;
}
if (changed) {
this.set('stats', {
min: stats.min,
max: stats.max
});
}
},
resetStats: function () {
this.unset('stats');
this.seriesCollection.forEach(function (series) {
if (series.has('stats')) {
this.updateStats(series.get('stats'));
}
}, this);
},
trackSeries: function (series) {
this.listenTo(series, 'change:stats', seriesStats => {
if (!seriesStats) {
this.resetStats();
} else {
this.updateStats(seriesStats);
}
});
this.listenTo(series, 'change:yKey', () => {
this.updateFromSeries(this.seriesCollection);
});
},
untrackSeries: function (series) {
this.stopListening(series);
this.resetStats();
this.updateFromSeries(this.seriesCollection);
},
toggleAutoscale: function (autoscale) {
if (autoscale && this.has('stats')) {
this.set('displayRange', this.applyPadding(this.get('stats')));
} else {
this.set('displayRange', this.get('range'));
}
},
/**
* Update yAxis format, values, and label from known series.
*/
updateFromSeries: function (series) {
this.unset('displayRange');
const plotModel = this.plot.get('domainObject');
const label = _.get(plotModel, 'configuration.yAxis.label');
const sampleSeries = series.first();
if (!sampleSeries) {
if (!label) {
this.unset('label');
}
return;
}
const yKey = sampleSeries.get('yKey');
const yMetadata = sampleSeries.metadata.value(yKey);
const yFormat = sampleSeries.formats[yKey];
this.set('format', yFormat.format.bind(yFormat));
this.set('values', yMetadata.values);
if (!label) {
const labelName = series.map(function (s) {
return s.metadata.value(s.get('yKey')).name;
}).reduce(function (a, b) {
if (a === undefined) {
return b;
}
if (a === b) {
return a;
}
return '';
}, undefined);
if (labelName) {
this.set('label', labelName);
return;
}
const labelUnits = series.map(function (s) {
return s.metadata.value(s.get('yKey')).units;
}).reduce(function (a, b) {
if (a === undefined) {
return b;
}
if (a === b) {
return a;
}
return '';
}, undefined);
if (labelUnits) {
this.set('label', labelUnits);
return;
}
}
},
defaults: function (options) {
return {
frozen: false,
autoscale: true,
autoscalePadding: 0.1
};
}
});
return YAxisModel;
});

View File

@ -1,48 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
], function (
) {
function ConfigStore() {
this.store = {};
}
ConfigStore.prototype.deleteStore = function (id) {
if (this.store[id]) {
this.store[id].destroy();
delete this.store[id];
}
};
ConfigStore.prototype.add = function (id, config) {
this.store[id] = config;
};
ConfigStore.prototype.get = function (id) {
return this.store[id];
};
const STORE = new ConfigStore();
return STORE;
});

View File

@ -1,163 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'EventEmitter',
'../lib/eventHelpers',
'./MarkerShapes'
], function (
EventEmitter,
eventHelpers,
MARKER_SHAPES
) {
/**
* Create a new draw API utilizing the Canvas's 2D API for rendering.
*
* @constructor
* @param {CanvasElement} canvas the canvas object to render upon
* @throws {Error} an error is thrown if Canvas's 2D API is unavailab
*/
function Draw2D(canvas) {
this.canvas = canvas;
this.c2d = canvas.getContext('2d');
this.width = canvas.width;
this.height = canvas.height;
this.dimensions = [this.width, this.height];
this.origin = [0, 0];
if (!this.c2d) {
throw new Error("Canvas 2d API unavailable.");
}
}
Object.assign(Draw2D.prototype, EventEmitter.prototype);
eventHelpers.extend(Draw2D.prototype);
// Convert from logical to physical x coordinates
Draw2D.prototype.x = function (v) {
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
};
// Convert from logical to physical y coordinates
Draw2D.prototype.y = function (v) {
return this.height
- ((v - this.origin[1]) / this.dimensions[1]) * this.height;
};
// Set the color to be used for drawing operations
Draw2D.prototype.setColor = function (color) {
const mappedColor = color.map(function (c, i) {
return i < 3 ? Math.floor(c * 255) : (c);
}).join(',');
this.c2d.strokeStyle = "rgba(" + mappedColor + ")";
this.c2d.fillStyle = "rgba(" + mappedColor + ")";
};
Draw2D.prototype.clear = function () {
this.width = this.canvas.width = this.canvas.offsetWidth;
this.height = this.canvas.height = this.canvas.offsetHeight;
this.c2d.clearRect(0, 0, this.width, this.height);
};
Draw2D.prototype.setDimensions = function (newDimensions, newOrigin) {
this.dimensions = newDimensions;
this.origin = newOrigin;
};
Draw2D.prototype.drawLine = function (buf, color, points) {
let i;
this.setColor(color);
// Configure context to draw two-pixel-thick lines
this.c2d.lineWidth = 1;
// Start a new path...
if (buf.length > 1) {
this.c2d.beginPath();
this.c2d.moveTo(this.x(buf[0]), this.y(buf[1]));
}
// ...and add points to it...
for (i = 2; i < points * 2; i = i + 2) {
this.c2d.lineTo(this.x(buf[i]), this.y(buf[i + 1]));
}
// ...before finally drawing it.
this.c2d.stroke();
};
Draw2D.prototype.drawSquare = function (min, max, color) {
const x1 = this.x(min[0]);
const y1 = this.y(min[1]);
const w = this.x(max[0]) - x1;
const h = this.y(max[1]) - y1;
this.setColor(color);
this.c2d.fillRect(x1, y1, w, h);
};
Draw2D.prototype.drawPoints = function (
buf,
color,
points,
pointSize,
shape
) {
const drawC2DShape = MARKER_SHAPES[shape].drawC2D.bind(this);
this.setColor(color);
for (let i = 0; i < points; i++) {
drawC2DShape(
this.x(buf[i * 2]),
this.y(buf[i * 2 + 1]),
pointSize
);
}
};
Draw2D.prototype.drawLimitPoint = function (x, y, size) {
this.c2d.fillRect(x + size, y, size, size);
this.c2d.fillRect(x, y + size, size, size);
this.c2d.fillRect(x - size, y, size, size);
this.c2d.fillRect(x, y - size, size, size);
};
Draw2D.prototype.drawLimitPoints = function (points, color, pointSize) {
const limitSize = pointSize * 2;
const offset = limitSize / 2;
this.setColor(color);
for (let i = 0; i < points.length; i++) {
this.drawLimitPoint(
this.x(points[i].x) - offset,
this.y(points[i].y) - offset,
limitSize
);
}
};
return Draw2D;
});

View File

@ -1,110 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define(
[
'./DrawWebGL',
'./Draw2D'
],
function (DrawWebGL, Draw2D) {
const CHARTS = [
{
MAX_INSTANCES: 16,
API: DrawWebGL,
ALLOCATIONS: []
},
{
MAX_INSTANCES: Number.POSITIVE_INFINITY,
API: Draw2D,
ALLOCATIONS: []
}
];
/**
* Draw loader attaches a draw API to a canvas element and returns the
* draw API.
*/
return {
/**
* Return the first draw API available. Returns
* `undefined` if a draw API could not be constructed.
*.
* @param {CanvasElement} canvas - The canvas eelement to attach
the draw API to.
*/
getDrawAPI: function (canvas, overlay) {
let api;
CHARTS.forEach(function (CHART_TYPE) {
if (api) {
return;
}
if (CHART_TYPE.ALLOCATIONS.length
>= CHART_TYPE.MAX_INSTANCES) {
return;
}
try {
api = new CHART_TYPE.API(canvas, overlay);
CHART_TYPE.ALLOCATIONS.push(api);
} catch (e) {
console.warn([
"Could not instantiate chart",
CHART_TYPE.API.name,
";",
e.message
].join(" "));
}
});
if (!api) {
console.warn("Cannot initialize mct-chart.");
}
return api;
},
/**
* Returns a fallback draw api.
*/
getFallbackDrawAPI: function (canvas, overlay) {
const api = new CHARTS[1].API(canvas, overlay);
CHARTS[1].ALLOCATIONS.push(api);
return api;
},
releaseDrawAPI: function (api) {
CHARTS.forEach(function (CHART_TYPE) {
if (api instanceof CHART_TYPE.API) {
CHART_TYPE.ALLOCATIONS.splice(CHART_TYPE.ALLOCATIONS.indexOf(api), 1);
}
});
if (api.destroy) {
api.destroy();
}
}
};
}
);

View File

@ -1,307 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'EventEmitter',
'../lib/eventHelpers',
'./MarkerShapes'
], function (
EventEmitter,
eventHelpers,
MARKER_SHAPES
) {
// WebGL shader sources (for drawing plain colors)
const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 uColor;
uniform int uMarkerShape;
void main(void) {
gl_FragColor = uColor;
if (uMarkerShape > 1) {
vec2 clipSpacePointCoord = 2.0 * gl_PointCoord - 1.0;
if (uMarkerShape == 2) { // circle
float distance = length(clipSpacePointCoord);
if (distance > 1.0) {
discard;
}
} else if (uMarkerShape == 3) { // diamond
float distance = abs(clipSpacePointCoord.x) + abs(clipSpacePointCoord.y);
if (distance > 1.0) {
discard;
}
} else if (uMarkerShape == 4) { // triangle
float x = clipSpacePointCoord.x;
float y = clipSpacePointCoord.y;
float distance = 2.0 * x - 1.0;
float distance2 = -2.0 * x - 1.0;
if (distance > y || distance2 > y) {
discard;
}
}
}
}
`;
const VERTEX_SHADER = `
attribute vec2 aVertexPosition;
uniform vec2 uDimensions;
uniform vec2 uOrigin;
uniform float uPointSize;
void main(void) {
gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);
gl_PointSize = uPointSize;
}
`;
/**
* Create a draw api utilizing WebGL.
*
* @constructor
* @param {CanvasElement} canvas the canvas object to render upon
* @throws {Error} an error is thrown if WebGL is unavailable.
*/
function DrawWebGL(canvas, overlay) {
this.canvas = canvas;
this.gl = this.canvas.getContext("webgl", { preserveDrawingBuffer: true })
|| this.canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true });
this.overlay = overlay;
this.c2d = overlay.getContext('2d');
if (!this.c2d) {
throw new Error("No canvas 2d!");
}
// Ensure a context was actually available before proceeding
if (!this.gl) {
throw new Error("WebGL unavailable.");
}
this.initContext();
this.listenTo(this.canvas, "webglcontextlost", this.onContextLost, this);
}
Object.assign(DrawWebGL.prototype, EventEmitter.prototype);
eventHelpers.extend(DrawWebGL.prototype);
DrawWebGL.prototype.onContextLost = function (event) {
this.emit('error');
this.isContextLost = true;
this.destroy();
};
DrawWebGL.prototype.initContext = function () {
// Initialize shaders
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
this.gl.compileShader(this.vertexShader);
this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
this.gl.shaderSource(this.fragmentShader, FRAGMENT_SHADER);
this.gl.compileShader(this.fragmentShader);
// Assemble vertex/fragment shaders into programs
this.program = this.gl.createProgram();
this.gl.attachShader(this.program, this.vertexShader);
this.gl.attachShader(this.program, this.fragmentShader);
this.gl.linkProgram(this.program);
this.gl.useProgram(this.program);
// Get locations for attribs/uniforms from the
// shader programs (to pass values into shaders at draw-time)
this.aVertexPosition = this.gl.getAttribLocation(this.program, "aVertexPosition");
this.uColor = this.gl.getUniformLocation(this.program, "uColor");
this.uMarkerShape = this.gl.getUniformLocation(this.program, "uMarkerShape");
this.uDimensions = this.gl.getUniformLocation(this.program, "uDimensions");
this.uOrigin = this.gl.getUniformLocation(this.program, "uOrigin");
this.uPointSize = this.gl.getUniformLocation(this.program, "uPointSize");
this.gl.enableVertexAttribArray(this.aVertexPosition);
// Create a buffer to holds points which will be drawn
this.buffer = this.gl.createBuffer();
// Enable blending, for smoothness
this.gl.enable(this.gl.BLEND);
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
};
DrawWebGL.prototype.destroy = function () {
this.stopListening();
};
// Convert from logical to physical x coordinates
DrawWebGL.prototype.x = function (v) {
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
};
// Convert from logical to physical y coordinates
DrawWebGL.prototype.y = function (v) {
return this.height
- ((v - this.origin[1]) / this.dimensions[1]) * this.height;
};
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points, shape) {
if (this.isContextLost) {
return;
}
const shapeCode = MARKER_SHAPES[shape] ? MARKER_SHAPES[shape].drawWebGL : 0;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
this.gl.uniform4fv(this.uColor, color);
this.gl.uniform1i(this.uMarkerShape, shapeCode);
this.gl.drawArrays(drawType, 0, points);
};
DrawWebGL.prototype.clear = function () {
if (this.isContextLost) {
return;
}
this.height = this.canvas.height = this.canvas.offsetHeight;
this.width = this.canvas.width = this.canvas.offsetWidth;
this.overlay.height = this.overlay.offsetHeight;
this.overlay.width = this.overlay.offsetWidth;
// Set the viewport size; note that we use the width/height
// that our WebGL context reports, which may be lower
// resolution than the canvas we requested.
this.gl.viewport(
0,
0,
this.gl.drawingBufferWidth,
this.gl.drawingBufferHeight
);
this.gl.clear(this.gl.COLOR_BUFFER_BIT + this.gl.DEPTH_BUFFER_BIT);
};
/**
* Set the logical boundaries of the chart.
* @param {number[]} dimensions the horizontal and
* vertical dimensions of the chart
* @param {number[]} origin the horizontal/vertical
* origin of the chart
*/
DrawWebGL.prototype.setDimensions = function (dimensions, origin) {
this.dimensions = dimensions;
this.origin = origin;
if (this.isContextLost) {
return;
}
if (dimensions && dimensions.length > 0
&& origin && origin.length > 0) {
this.gl.uniform2fv(this.uDimensions, dimensions);
this.gl.uniform2fv(this.uOrigin, origin);
}
};
/**
* Draw the supplied buffer as a line strip (a sequence
* of line segments), in the chosen color.
* @param {Float32Array} buf the line strip to draw,
* in alternating x/y positions
* @param {number[]} color the color to use when drawing
* the line, as an RGBA color where each element
* is in the range of 0.0-1.0
* @param {number} points the number of points to draw
*/
DrawWebGL.prototype.drawLine = function (buf, color, points) {
if (this.isContextLost) {
return;
}
this.doDraw(this.gl.LINE_STRIP, buf, color, points);
};
/**
* Draw the buffer as points.
*
*/
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
if (this.isContextLost) {
return;
}
this.gl.uniform1f(this.uPointSize, pointSize);
this.doDraw(this.gl.POINTS, buf, color, points, shape);
};
/**
* Draw a rectangle extending from one corner to another,
* in the chosen color.
* @param {number[]} min the first corner of the rectangle
* @param {number[]} max the opposite corner
* @param {number[]} color the color to use when drawing
* the rectangle, as an RGBA color where each element
* is in the range of 0.0-1.0
*/
DrawWebGL.prototype.drawSquare = function (min, max, color) {
if (this.isContextLost) {
return;
}
this.doDraw(this.gl.TRIANGLE_FAN, new Float32Array(
min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])
), color, 4);
};
DrawWebGL.prototype.drawLimitPoint = function (x, y, size) {
this.c2d.fillRect(x + size, y, size, size);
this.c2d.fillRect(x, y + size, size, size);
this.c2d.fillRect(x - size, y, size, size);
this.c2d.fillRect(x, y - size, size, size);
};
DrawWebGL.prototype.drawLimitPoints = function (points, color, pointSize) {
const limitSize = pointSize * 2;
const offset = limitSize / 2;
const mappedColor = color.map(function (c, i) {
return i < 3 ? Math.floor(c * 255) : (c);
}).join(',');
this.c2d.strokeStyle = "rgba(" + mappedColor + ")";
this.c2d.fillStyle = "rgba(" + mappedColor + ")";
for (let i = 0; i < points.length; i++) {
this.drawLimitPoint(
this.x(points[i].x) - offset,
this.y(points[i].y) - offset,
limitSize
);
}
};
return DrawWebGL;
});

View File

@ -1,90 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([], function () {
/**
* @label string (required) display name of shape
* @drawWebGL integer (unique, required) index provided to WebGL Fragment Shader
* @drawC2D function (required) canvas2d draw function
*/
const MARKER_SHAPES = {
point: {
label: 'Point',
drawWebGL: 1,
drawC2D: function (x, y, size) {
const offset = size / 2;
this.c2d.fillRect(x - offset, y - offset, size, size);
}
},
circle: {
label: 'Circle',
drawWebGL: 2,
drawC2D: function (x, y, size) {
const radius = size / 2;
this.c2d.beginPath();
this.c2d.arc(x, y, radius, 0, 2 * Math.PI, false);
this.c2d.closePath();
this.c2d.fill();
}
},
diamond: {
label: 'Diamond',
drawWebGL: 3,
drawC2D: function (x, y, size) {
const offset = size / 2;
const top = [x, y + offset];
const right = [x + offset, y];
const bottom = [x, y - offset];
const left = [x - offset, y];
this.c2d.beginPath();
this.c2d.moveTo(...top);
this.c2d.lineTo(...right);
this.c2d.lineTo(...bottom);
this.c2d.lineTo(...left);
this.c2d.closePath();
this.c2d.fill();
}
},
triangle: {
label: 'Triangle',
drawWebGL: 4,
drawC2D: function (x, y, size) {
const offset = size / 2;
const v1 = [x, y - offset];
const v2 = [x - offset, y + offset];
const v3 = [x + offset, y + offset];
this.c2d.beginPath();
this.c2d.moveTo(...v1);
this.c2d.lineTo(...v2);
this.c2d.lineTo(...v3);
this.c2d.closePath();
this.c2d.fill();
}
}
};
return MARKER_SHAPES;
});

View File

@ -1,55 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define(function () {
/**
* Simple directive that removes the elements pool when used in the
* inspector region. Workaround until we have better control of screen
* regions.
*/
return function HideElementPoolDirective() {
return {
restrict: "A",
link: function ($scope, $element) {
let splitter = $element.parent();
while (splitter[0].tagName !== 'MCT-SPLIT-PANE') {
splitter = splitter.parent();
}
[
'.split-pane-component.pane.bottom',
'mct-splitter'
].forEach(function (selector) {
const element = splitter[0].querySelectorAll(selector)[0];
element.style.maxHeight = '0px';
element.style.minHeight = '0px';
});
splitter[0]
.querySelectorAll('.split-pane-component.pane.top')[0]
.style
.bottom = '0px';
}
};
};
});

View File

@ -1,67 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define(
[
'./Region'
],
function (Region) {
/**
* Defines the a default Inspector region. Captured in a class to
* allow for modular extension and customization of regions based on
* the typical case.
* @memberOf platform/commonUI/regions
* @constructor
*/
function InspectorRegion() {
Region.call(this, {'name': 'Inspector'});
this.buildRegion();
}
InspectorRegion.prototype = Object.create(Region.prototype);
InspectorRegion.prototype.constructor = Region;
/**
* @private
*/
InspectorRegion.prototype.buildRegion = function () {
const metadataRegion = {
name: 'metadata',
title: 'Metadata Region',
// Which modes should the region part be visible in? If
// nothing provided here, then assumed that part is visible
// in both. The visibility or otherwise of a region part
// should be decided by a policy. In this case, 'modes' is a
// shortcut that is used by the EditableRegionPolicy.
modes: ['browse', 'edit'],
content: {
key: 'object-properties'
}
};
this.addRegion(new Region(metadataRegion), 0);
};
return InspectorRegion;
}
);

View File

@ -1,41 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./Region'
], function (
Region
) {
const PlotBrowseRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['browse'],
content: {
key: "plot-options-browse"
}
});
return PlotBrowseRegion;
});

View File

@ -1,39 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open openmct 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 openmct 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.
*****************************************************************************/
define([
'./Region'
], function (
Region
) {
const PlotEditRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['edit'],
content: {
key: "plot-options-edit"
}
});
return PlotEditRegion;
});

View File

@ -1,39 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./InspectorRegion',
'./PlotBrowseRegion',
'./PlotEditRegion'
], function (
InspectorRegion,
PlotBrowseRegion,
PlotEditRegion
) {
const plotInspector = new InspectorRegion();
plotInspector.addRegion(PlotBrowseRegion);
plotInspector.addRegion(PlotEditRegion);
return plotInspector;
});

View File

@ -1,79 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./PlotModelFormController'
], function (
PlotModelFormController
) {
const PlotLegendFormController = PlotModelFormController.extend({
fields: [
{
modelProp: 'position',
objectPath: 'configuration.legend.position'
},
{
modelProp: 'hideLegendWhenSmall',
coerce: Boolean,
objectPath: 'configuration.legend.hideLegendWhenSmall'
},
{
modelProp: 'expandByDefault',
coerce: Boolean,
objectPath: 'configuration.legend.expandByDefault'
},
{
modelProp: 'valueToShowWhenCollapsed',
objectPath: 'configuration.legend.valueToShowWhenCollapsed'
},
{
modelProp: 'showValueWhenExpanded',
coerce: Boolean,
objectPath: 'configuration.legend.showValueWhenExpanded'
},
{
modelProp: 'showTimestampWhenExpanded',
coerce: Boolean,
objectPath: 'configuration.legend.showTimestampWhenExpanded'
},
{
modelProp: 'showMaximumWhenExpanded',
coerce: Boolean,
objectPath: 'configuration.legend.showMaximumWhenExpanded'
},
{
modelProp: 'showMinimumWhenExpanded',
coerce: Boolean,
objectPath: 'configuration.legend.showMinimumWhenExpanded'
},
{
modelProp: 'showUnitsWhenExpanded',
coerce: Boolean,
objectPath: 'configuration.legend.showUnitsWhenExpanded'
}
]
});
return PlotLegendFormController;
});

View File

@ -1,181 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'../lib/eventHelpers',
'../lib/extend',
'lodash'
], function (
eventHelpers,
extend,
_
) {
/**
* Generic type for model controllers. Should be extended to build a form
* for a specific model.
*/
function PlotModelFormController($scope, openmct, attrs) {
this.$scope = $scope;
this.openmct = openmct;
this.attrs = attrs;
if (this.isReady()) {
this.initializeScope();
} else {
this.$scope.$watch(this.isReady.bind(this), function (isReady) {
if (isReady) {
this.initializeScope();
}
}.bind(this));
}
}
PlotModelFormController.extend = extend;
eventHelpers.extend(PlotModelFormController.prototype);
PlotModelFormController.prototype.isReady = function () {
return Boolean(this.$scope.formDomainObject)
&& Boolean(this.$scope.$eval(this.attrs.formModel));
};
/**
* Initialize scope is called when the formDomainObject has been set.
* This may be deferred until after the controller construction in cases
* where the object has not yet loaded.
*/
PlotModelFormController.prototype.initializeScope = function () {
this.domainObject = this.$scope.formDomainObject;
this.model = this.$scope.$eval(this.attrs.formModel);
this.unlisten = this.openmct.objects.observe(
this.domainObject,
'*',
this.updateDomainObject.bind(this)
);
this.$scope.form = {};
this.$scope.validation = {};
this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.initialize();
this.initForm();
};
PlotModelFormController.prototype.updateDomainObject = function (domainObject) {
this.domainObject = domainObject;
};
PlotModelFormController.prototype.destroy = function () {
this.stopListening();
this.model.stopListening(this.$scope);
this.unlisten();
};
PlotModelFormController.prototype.fields = [];
/** override for custom initializer **/
PlotModelFormController.prototype.initialize = function () {
};
PlotModelFormController.prototype.initForm = function () {
this.fields.forEach(function (field) {
this.linkFields(
field.modelProp,
field.formProp,
field.coerce,
field.validate,
field.objectPath
);
}, this);
};
PlotModelFormController.prototype.linkFields = function (
prop,
formProp,
coerce,
validate,
objectPath
) {
if (!formProp) {
formProp = prop;
}
const formPath = 'form.' + formProp;
let self = this;
if (!coerce) {
coerce = function (v) {
return v;
};
}
if (!validate) {
validate = function () {
return true;
};
}
if (objectPath && (typeof objectPath !== "function")) {
const staticObjectPath = objectPath;
objectPath = function () {
return staticObjectPath;
};
}
this.listenTo(this.model, 'change:' + prop, function (newVal, oldVal) {
if (!_.isEqual(coerce(_.get(self.$scope, formPath)), coerce(newVal))) {
_.set(self.$scope, formPath, coerce(newVal));
}
});
this.model.listenTo(this.$scope, 'change:' + formPath, (newVal, oldVal) => {
const validationResult = validate(newVal, this.model);
if (validationResult === true) {
delete this.$scope.validation[formProp];
} else {
this.$scope.validation[formProp] = validationResult;
return;
}
if (_.isEqual(coerce(newVal), coerce(this.model.get(prop)))) {
return; // Don't trigger excessive mutations.
}
if (!_.isEqual(coerce(newVal), coerce(oldVal))) {
this.model.set(prop, coerce(newVal));
if (objectPath) {
this.openmct.objects.mutate(
this.domainObject,
objectPath(this.domainObject, this.model),
coerce(newVal)
);
}
}
});
_.set(this.$scope, formPath, coerce(this.model.get(prop)));
};
return PlotModelFormController;
});

View File

@ -1,93 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'../configuration/configStore',
'../lib/eventHelpers',
'objectUtils'
], function (
configStore,
eventHelpers,
objectUtils
) {
function PlotOptionsController($scope, openmct, $timeout) {
this.$scope = $scope;
this.openmct = openmct;
this.$timeout = $timeout;
this.configId = $scope.domainObject.getId();
this.setUpScope();
}
eventHelpers.extend(PlotOptionsController.prototype);
PlotOptionsController.prototype.updateDomainObject = function (domainObject) {
this.domainObject = domainObject;
this.$scope.formDomainObject = domainObject;
};
PlotOptionsController.prototype.destroy = function () {
this.stopListening();
this.unlisten();
};
PlotOptionsController.prototype.setUpScope = function () {
const config = configStore.get(this.configId);
if (!config) {
this.$timeout(this.setUpScope.bind(this));
return;
}
this.config = this.$scope.config = config;
this.$scope.plotSeries = [];
this.updateDomainObject(this.config.get('domainObject'));
this.unlisten = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject.bind(this));
this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.listenTo(config.series, 'add', this.addSeries, this);
this.listenTo(config.series, 'remove', this.resetAllSeries, this);
config.series.forEach(this.addSeries, this);
};
PlotOptionsController.prototype.addSeries = function (series, index) {
this.$timeout(function () {
this.$scope.plotSeries[index] = series;
series.locateOldObject(this.$scope.domainObject);
}.bind(this));
};
PlotOptionsController.prototype.resetAllSeries = function (series, index) {
this.$timeout(function () {
this.$scope.plotSeries = [];
this.$timeout(function () {
this.config.series.forEach(this.addSeries, this);
}.bind(this));
}.bind(this));
};
return PlotOptionsController;
});

View File

@ -1,139 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./PlotModelFormController',
'../draw/MarkerShapes',
'lodash'
], function (
PlotModelFormController,
MARKER_SHAPES,
_
) {
function dynamicPathForKey(key) {
return function (object, model) {
const modelIdentifier = model.get('identifier');
const index = object.configuration.series.findIndex(s => {
return _.isEqual(s.identifier, modelIdentifier);
});
return 'configuration.series[' + index + '].' + key;
};
}
const PlotSeriesFormController = PlotModelFormController.extend({
/**
* Set the color for the current plot series. If the new color was
* already assigned to a different plot series, then swap the colors.
*/
setColor: function (color) {
const oldColor = this.model.get('color');
const otherSeriesWithColor = this.model.collection.filter(function (s) {
return s.get('color') === color;
})[0];
this.model.set('color', color);
const getPath = dynamicPathForKey('color');
const seriesColorPath = getPath(this.domainObject, this.model);
this.openmct.objects.mutate(
this.domainObject,
seriesColorPath,
color.asHexString()
);
if (otherSeriesWithColor) {
otherSeriesWithColor.set('color', oldColor);
const otherSeriesColorPath = getPath(
this.domainObject,
otherSeriesWithColor
);
this.openmct.objects.mutate(
this.domainObject,
otherSeriesColorPath,
oldColor.asHexString()
);
}
},
/**
* Populate scope with options and add setColor callback.
*/
initialize: function () {
this.$scope.setColor = this.setColor.bind(this);
const metadata = this.model.metadata;
this.$scope.yKeyOptions = metadata
.valuesForHints(['range'])
.map(function (o) {
return {
name: o.key,
value: o.key
};
});
this.$scope.markerShapeOptions = Object.entries(MARKER_SHAPES)
.map(([key, obj]) => {
return {
name: obj.label,
value: key
};
});
},
fields: [
{
modelProp: 'yKey',
objectPath: dynamicPathForKey('yKey')
},
{
modelProp: 'interpolate',
objectPath: dynamicPathForKey('interpolate')
},
{
modelProp: 'markers',
objectPath: dynamicPathForKey('markers')
},
{
modelProp: 'markerShape',
objectPath: dynamicPathForKey('markerShape')
},
{
modelProp: 'markerSize',
coerce: Number,
objectPath: dynamicPathForKey('markerSize')
},
{
modelProp: 'alarmMarkers',
coerce: Boolean,
objectPath: dynamicPathForKey('alarmMarkers')
}
]
});
return PlotSeriesFormController;
});

View File

@ -1,104 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./PlotModelFormController'
], function (
PlotModelFormController
) {
const PlotYAxisFormController = PlotModelFormController.extend({
fields: [
{
modelProp: 'label',
objectPath: 'configuration.yAxis.label'
},
{
modelProp: 'autoscale',
coerce: Boolean,
objectPath: 'configuration.yAxis.autoscale'
},
{
modelProp: 'autoscalePadding',
coerce: Number,
objectPath: 'configuration.yAxis.autoscalePadding'
},
{
modelProp: 'range',
objectPath: 'configuration.yAxis.range',
coerce: function coerceRange(range) {
if (!range) {
return {
min: 0,
max: 0
};
}
const newRange = {};
if (typeof range.min !== 'undefined' && range.min !== null) {
newRange.min = Number(range.min);
}
if (typeof range.max !== 'undefined' && range.max !== null) {
newRange.max = Number(range.max);
}
return newRange;
},
validate: function validateRange(range, model) {
if (!range) {
return 'Need range';
}
if (range.min === '' || range.min === null || typeof range.min === 'undefined') {
return 'Must specify Minimum';
}
if (range.max === '' || range.max === null || typeof range.max === 'undefined') {
return 'Must specify Maximum';
}
if (Number.isNaN(Number(range.min))) {
return 'Minimum must be a number.';
}
if (Number.isNaN(Number(range.max))) {
return 'Maximum must be a number.';
}
if (Number(range.min) > Number(range.max)) {
return 'Minimum must be less than Maximum.';
}
if (model.get('autoscale')) {
return false;
}
return true;
}
}
]
});
return PlotYAxisFormController;
});

View File

@ -1,99 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define(
[],
function () {
/**
* @typeDef {object} PartContents
* @property {string} key If the part is defined as a
* representation, the key corresponding to the representation.
* @memberOf platform/commonUI/regions
*/
/**
* @typeDef {object} RegionConfiguration
* @property {string} name A unique name for this region part
* @property {PartContents} [content] the details of the region being
* defined
* @property {Array<string>} [modes] the modes that this region
* should be included in. Options are 'edit' and 'browse'. By
* default, will be included in both. Inclusion of regions is
* determined by policies of category 'region'. By default, the
* {EditableRegionPolicy} will be applied.
* @memberOf platform/commonUI/regions
*/
/**
* Defines the interface for a screen region. A screen region is a
* section of the browse an edit screens for an object. Regions are
* declared in object type definitions.
* @memberOf platform/commonUI/regions
* @abstract
* @constructor
*/
function Region(configuration) {
configuration = configuration || {};
this.name = configuration.name;
this.content = configuration.content;
this.modes = configuration.modes;
this.regions = [];
}
/**
* Adds a sub-region to this region.
* @param {Region} region the part to add
* @param {number} [index] the position to insert the region. By default
* will add to the end
*/
Region.prototype.addRegion = function (region, index) {
if (index) {
this.regions.splice(index, 0, region);
} else {
this.regions.push(region);
}
};
/**
* Removes a sub-region from this region.
* @param {Region | number | strnig} region The region to
* remove. If a number, will remove the region at that index. If a
* string, will remove the region with the matching name. If an
* object, will attempt to remove that object from the Region
*/
Region.prototype.removeRegion = function (region) {
if (typeof region === 'number') {
this.regions.splice(region, 1);
} else if (typeof region === 'string') {
this.regions = this.regions.filter(function (thisRegion) {
return thisRegion.name !== region;
});
} else {
this.regions.splice(this.regions.indexOf(region), 1);
}
};
return Region;
}
);

View File

@ -1,208 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define(function () {
const COLOR_PALETTE = [
[0x20, 0xB2, 0xAA],
[0x9A, 0xCD, 0x32],
[0xFF, 0x8C, 0x00],
[0xD2, 0xB4, 0x8C],
[0x40, 0xE0, 0xD0],
[0x41, 0x69, 0xFF],
[0xFF, 0xD7, 0x00],
[0x6A, 0x5A, 0xCD],
[0xEE, 0x82, 0xEE],
[0xCC, 0x99, 0x66],
[0x99, 0xCC, 0xCC],
[0x66, 0xCC, 0x33],
[0xFF, 0xCC, 0x00],
[0xFF, 0x66, 0x33],
[0xCC, 0x66, 0xFF],
[0xFF, 0x00, 0x66],
[0xFF, 0xFF, 0x00],
[0x80, 0x00, 0x80],
[0x00, 0x86, 0x8B],
[0x00, 0x8A, 0x00],
[0xFF, 0x00, 0x00],
[0x00, 0x00, 0xFF],
[0xF5, 0xDE, 0xB3],
[0xBC, 0x8F, 0x8F],
[0x46, 0x82, 0xB4],
[0xFF, 0xAF, 0xAF],
[0x43, 0xCD, 0x80],
[0xCD, 0xC1, 0xC5],
[0xA0, 0x52, 0x2D],
[0x64, 0x95, 0xED]
];
function isDefaultColor(color) {
const a = color.asIntegerArray();
return COLOR_PALETTE.some(function (b) {
return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
});
}
/**
* A representation of a color that allows conversions between different
* formats.
*
* @constructor
*/
function Color(integerArray) {
this.integerArray = integerArray;
}
Color.fromHexString = function (hexString) {
if (!/#([0-9a-fA-F]{2}){2}/.test(hexString)) {
throw new Error(
'Invalid input "'
+ hexString
+ '". Hex string must be in CSS format e.g. #00FF00'
);
}
return new Color([
parseInt(hexString.slice(1, 3), 16),
parseInt(hexString.slice(3, 5), 16),
parseInt(hexString.slice(5, 7), 16)
]);
};
/**
* Return color as a three element array of RGB values, where each value
* is a integer in the range of 0-255.
*
* @return {number[]} the color, as integer RGB values
*/
Color.prototype.asIntegerArray = function () {
return this.integerArray.map(function (c) {
return c;
});
};
/**
* Return color as a string using #-prefixed six-digit RGB hex notation
* (e.g. #FF0000). See http://www.w3.org/TR/css3-color/#rgb-color.
*
* @return {string} the color, as a style-friendly string
*/
Color.prototype.asHexString = function () {
return '#' + this.integerArray.map(function (c) {
return (c < 16 ? '0' : '') + c.toString(16);
}).join('');
};
/**
* Return color as a RGBA float array.
*
* This format is present specifically to support use with
* WebGL, which expects colors of that form.
*
* @return {number[]} the color, as floating-point RGBA values
*/
Color.prototype.asRGBAArray = function () {
return this.integerArray.map(function (c) {
return c / 255.0;
}).concat([1]);
};
Color.prototype.equalTo = function (otherColor) {
return this.asHexString() === otherColor.asHexString();
};
/**
* A color palette stores a set of colors and allows for different
* methods of color allocation.
*
* @constructor
*/
function ColorPalette() {
const allColors = this.allColors = COLOR_PALETTE.map(function (color) {
return new Color(color);
});
this.colorGroups = [[], [], []];
for (let i = 0; i < allColors.length; i++) {
this.colorGroups[i % 3].push(allColors[i]);
}
this.reset();
}
/**
*
*/
ColorPalette.prototype.groups = function () {
return this.colorGroups;
};
ColorPalette.prototype.reset = function () {
this.availableColors = this.allColors.slice();
};
ColorPalette.prototype.remove = function (color) {
this.availableColors = this.availableColors.filter(function (c) {
return !c.equalTo(color);
});
};
ColorPalette.prototype.return = function (color) {
if (isDefaultColor(color)) {
this.availableColors.unshift(color);
}
};
ColorPalette.prototype.getByHexString = function (hexString) {
const color = Color.fromHexString(hexString);
return color;
};
/**
* @returns {Color} the next unused color in the palette. If all colors
* have been allocated, it will wrap around.
*/
ColorPalette.prototype.getNextColor = function () {
if (!this.availableColors.length) {
console.warn('Color Palette empty, reusing colors!');
this.reset();
}
return this.availableColors.shift();
};
/**
* @param {number} index the index of the color to return. An index
* value larger than the size of the index will wrap around.
* @returns {Color}
*/
ColorPalette.prototype.getColor = function (index) {
return this.colors[index % this.colors.length];
};
return {
Color: Color,
ColorPalette: ColorPalette
};
});

View File

@ -1,104 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/*jscs:disable disallowDanglingUnderscores */
define([
], function (
) {
const helperFunctions = {
listenTo: function (object, event, callback, context) {
if (!this._listeningTo) {
this._listeningTo = [];
}
const listener = {
object: object,
event: event,
callback: callback,
context: context,
_cb: context ? callback.bind(context) : callback
};
if (object.$watch && event.indexOf('change:') === 0) {
const scopePath = event.replace('change:', '');
listener.unlisten = object.$watch(scopePath, listener._cb, true);
} else if (object.$on) {
listener.unlisten = object.$on(event, listener._cb);
} else if (object.addEventListener) {
object.addEventListener(event, listener._cb);
} else {
object.on(event, listener._cb);
}
this._listeningTo.push(listener);
},
stopListening: function (object, event, callback, context) {
if (!this._listeningTo) {
this._listeningTo = [];
}
this._listeningTo.filter(function (listener) {
if (object && object !== listener.object) {
return false;
}
if (event && event !== listener.event) {
return false;
}
if (callback && callback !== listener.callback) {
return false;
}
if (context && context !== listener.context) {
return false;
}
return true;
})
.map(function (listener) {
if (listener.unlisten) {
listener.unlisten();
} else if (listener.object.removeEventListener) {
listener.object.removeEventListener(listener.event, listener._cb);
} else {
listener.object.off(listener.event, listener._cb);
}
return listener;
})
.forEach(function (listener) {
this._listeningTo.splice(this._listeningTo.indexOf(listener), 1);
}, this);
},
extend: function (object) {
object.listenTo = helperFunctions.listenTo;
object.stopListening = helperFunctions.stopListening;
}
};
return helperFunctions;
});

View File

@ -1,70 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/*jscs:disable disallowDanglingUnderscores */
define([
], function (
) {
function extend(props) {
// eslint-disable-next-line no-invalid-this
const parent = this;
let child;
let Surrogate;
if (props && Object.prototype.hasOwnProperty.call(props, 'constructor')) {
child = props.constructor;
} else {
child = function () {
// eslint-disable-next-line no-invalid-this
return parent.apply(this, arguments);
};
}
Object.keys(parent).forEach(function copyStaticProperties(propKey) {
child[propKey] = parent[propKey];
});
// Surrogate allows inheriting from parent without invoking constructor.
Surrogate = function () {
this.constructor = child;
};
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate();
if (props) {
Object.keys(props).forEach(function copyInstanceProperties(key) {
child.prototype[key] = props[key];
});
}
child.__super__ = parent.prototype;
return child;
}
return extend;
});

View File

@ -1,88 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/*jscs:disable disallowDanglingUnderscores */
define([
], function (
) {
/**
* A scale has an input domain and an output range. It provides functions
* `scale` return the range value associated with a domain value.
* `invert` return the domain value associated with range value.
*/
function LinearScale(domain) {
this.domain(domain);
}
LinearScale.prototype.domain = function (newDomain) {
if (newDomain) {
this._domain = newDomain;
this._domainDenominator = newDomain.max - newDomain.min;
}
return this._domain;
};
LinearScale.prototype.range = function (newRange) {
if (newRange) {
this._range = newRange;
this._rangeDenominator = newRange.max - newRange.min;
}
return this._range;
};
LinearScale.prototype.scale = function (domainValue) {
if (!this._domain || !this._range) {
return;
}
const domainOffset = domainValue - this._domain.min;
const rangeFraction = domainOffset - this._domainDenominator;
const rangeOffset = rangeFraction * this._rangeDenominator;
const rangeValue = rangeOffset + this._range.min;
return rangeValue;
};
LinearScale.prototype.invert = function (rangeValue) {
if (!this._domain || !this._range) {
return;
}
const rangeOffset = rangeValue - this._range.min;
const domainFraction = rangeOffset / this._rangeDenominator;
const domainOffset = domainFraction * this._domainDenominator;
const domainValue = domainOffset + this._domain.min;
return domainValue;
};
return LinearScale;
});
/**
*
*/

View File

@ -1,616 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./LinearScale',
'../lib/eventHelpers'
], function (
LinearScale,
eventHelpers
) {
/**
* MCTPlotController handles user interactions with the plot canvas.
* It supports pan and zoom, implements zoom history, and supports locating
* values near the cursor.
*/
function MCTPlotController($scope, $element, $window) {
this.$onInit = () => {
this.$scope = $scope;
this.$scope.config = this.config;
this.$scope.plot = this;
this.$element = $element;
this.$window = $window;
this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
this.pan = undefined;
this.marquee = undefined;
this.chartElementBounds = undefined;
this.tickUpdate = false;
this.$scope.plotHistory = this.plotHistory = [];
this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this);
this.initialize();
};
}
MCTPlotController.$inject = ['$scope', '$element', '$window'];
eventHelpers.extend(MCTPlotController.prototype);
MCTPlotController.prototype.initCanvas = function () {
if (this.$canvas) {
this.stopListening(this.$canvas);
}
this.$canvas = this.$element.find('canvas');
this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this);
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
};
MCTPlotController.prototype.initialize = function () {
this.$canvas = this.$element.find('canvas');
this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this);
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
this.$scope.rectangles = [];
this.$scope.tickWidth = 0;
this.$scope.xAxis = this.config.xAxis;
this.$scope.yAxis = this.config.yAxis;
this.$scope.series = this.config.series.models;
this.$scope.legend = this.config.legend;
this.$scope.yAxisLabel = this.config.yAxis.get('label');
this.cursorGuideVertical = this.$element[0].querySelector('.js-cursor-guide--v');
this.cursorGuideHorizontal = this.$element[0].querySelector('.js-cursor-guide--h');
this.cursorGuide = false;
this.gridLines = true;
this.listenTo(this.$scope, 'cursorguide', this.toggleCursorGuide, this);
this.listenTo(this.$scope, 'toggleGridLines', this.toggleGridLines, this);
this.listenTo(this.$scope, '$destroy', this.destroy, this);
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;
this.$scope.yKeyOptions = metadata
.valuesForHints(['range'])
.map(function (o) {
return {
name: o.name,
key: o.key
};
});
// set yAxisLabel if none is set yet
if (this.$scope.yAxisLabel === 'none') {
let yKey = this.$scope.series[0].model.yKey;
let yKeyModel = this.$scope.yKeyOptions.filter(o => o.key === yKey)[0];
this.$scope.yAxisLabel = yKeyModel.name;
}
} else {
this.$scope.yKeyOptions = undefined;
}
};
MCTPlotController.prototype.onXAxisChange = function (displayBounds) {
if (displayBounds) {
this.xScale.domain(displayBounds);
}
};
MCTPlotController.prototype.onYAxisChange = function (displayBounds) {
if (displayBounds) {
this.yScale.domain(displayBounds);
}
};
MCTPlotController.prototype.onTickWidthChange = function ($event, width) {
if ($event.targetScope.domainObject !== this.$scope.domainObject) {
// Always accept tick width if it comes from a different object.
this.$scope.tickWidth = width;
} else {
// Otherwise, only accept tick with if it's larger.
const newWidth = Math.max(width, this.$scope.tickWidth);
if (newWidth !== this.$scope.tickWidth) {
this.$scope.tickWidth = newWidth;
this.$scope.$digest();
}
}
};
MCTPlotController.prototype.trackMousePosition = function ($event) {
this.trackChartElementBounds($event);
this.xScale.range({
min: 0,
max: this.chartElementBounds.width
});
this.yScale.range({
min: 0,
max: this.chartElementBounds.height
});
this.positionOverElement = {
x: $event.clientX - this.chartElementBounds.left,
y: this.chartElementBounds.height
- ($event.clientY - this.chartElementBounds.top)
};
this.positionOverPlot = {
x: this.xScale.invert(this.positionOverElement.x),
y: this.yScale.invert(this.positionOverElement.y)
};
if (this.cursorGuide) {
this.updateCrosshairs($event);
}
this.highlightValues(this.positionOverPlot.x);
this.updateMarquee();
this.updatePan();
this.$scope.$digest();
$event.preventDefault();
};
MCTPlotController.prototype.updateCrosshairs = function ($event) {
this.cursorGuideVertical.style.left = ($event.clientX - this.chartElementBounds.x) + 'px';
this.cursorGuideHorizontal.style.top = ($event.clientY - this.chartElementBounds.y) + 'px';
};
MCTPlotController.prototype.trackChartElementBounds = function ($event) {
if ($event.target === this.$canvas[1]) {
this.chartElementBounds = $event.target.getBoundingClientRect();
}
};
MCTPlotController.prototype.onPlotHighlightSet = function ($e, point) {
if (point === this.highlightPoint) {
return;
}
this.highlightValues(point);
};
MCTPlotController.prototype.highlightValues = function (point) {
this.highlightPoint = point;
this.$scope.$emit('plot:highlight:update', point);
if (this.$scope.lockHighlightPoint) {
return;
}
if (!point) {
this.$scope.highlights = [];
this.$scope.series.forEach(series => delete series.closest);
} else {
this.$scope.highlights = this.$scope.series
.filter(series => series.data.length > 0)
.map(series => {
series.closest = series.nearestPoint(point);
return {
series: series,
point: series.closest
};
});
}
this.$scope.$digest();
};
MCTPlotController.prototype.untrackMousePosition = function () {
this.positionOverElement = undefined;
this.positionOverPlot = undefined;
this.highlightValues();
};
MCTPlotController.prototype.onMouseDown = function ($event) {
// do not monitor drag events on browser context click
if (event.ctrlKey) {
return;
}
this.listenTo(this.$window, 'mouseup', this.onMouseUp, this);
this.listenTo(this.$window, 'mousemove', this.trackMousePosition, this);
if (event.altKey) {
return this.startPan($event);
} else {
return this.startMarquee($event);
}
};
MCTPlotController.prototype.onMouseUp = function ($event) {
this.stopListening(this.$window, 'mouseup', this.onMouseUp, this);
this.stopListening(this.$window, 'mousemove', this.trackMousePosition, this);
if (this.isMouseClick()) {
this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
}
if (this.pan) {
return this.endPan($event);
}
if (this.marquee) {
return this.endMarquee($event);
}
};
MCTPlotController.prototype.isMouseClick = function () {
if (!this.marquee) {
return false;
}
const { start, end } = this.marquee;
return start.x === end.x && start.y === end.y;
};
MCTPlotController.prototype.updateMarquee = function () {
if (!this.marquee) {
return;
}
this.marquee.end = this.positionOverPlot;
this.marquee.endPixels = this.positionOverElement;
};
MCTPlotController.prototype.startMarquee = function ($event) {
this.$canvas.removeClass('plot-drag');
this.$canvas.addClass('plot-marquee');
this.trackMousePosition($event);
if (this.positionOverPlot) {
this.freeze();
this.marquee = {
startPixels: this.positionOverElement,
endPixels: this.positionOverElement,
start: this.positionOverPlot,
end: this.positionOverPlot,
color: [1, 1, 1, 0.5]
};
this.$scope.rectangles.push(this.marquee);
this.trackHistory();
}
};
MCTPlotController.prototype.endMarquee = function () {
const startPixels = this.marquee.startPixels;
const endPixels = this.marquee.endPixels;
const marqueeDistance = Math.sqrt(
Math.pow(startPixels.x - endPixels.x, 2)
+ Math.pow(startPixels.y - endPixels.y, 2)
);
// Don't zoom if mouse moved less than 7.5 pixels.
if (marqueeDistance > 7.5) {
this.$scope.xAxis.set('displayRange', {
min: Math.min(this.marquee.start.x, this.marquee.end.x),
max: Math.max(this.marquee.start.x, this.marquee.end.x)
});
this.$scope.yAxis.set('displayRange', {
min: Math.min(this.marquee.start.y, this.marquee.end.y),
max: Math.max(this.marquee.start.y, this.marquee.end.y)
});
this.$scope.$emit('user:viewport:change:end');
} else {
// A history entry is created by startMarquee, need to remove
// if marquee zoom doesn't occur.
this.plotHistory.pop();
}
this.$scope.rectangles = [];
this.marquee = undefined;
};
MCTPlotController.prototype.zoom = function (zoomDirection, zoomFactor) {
const currentXaxis = this.$scope.xAxis.get('displayRange');
const currentYaxis = this.$scope.yAxis.get('displayRange');
// when there is no plot data, the ranges can be undefined
// in which case we should not perform zoom
if (!currentXaxis || !currentYaxis) {
return;
}
this.freeze();
this.trackHistory();
const xAxisDist = (currentXaxis.max - currentXaxis.min) * zoomFactor;
const yAxisDist = (currentYaxis.max - currentYaxis.min) * zoomFactor;
if (zoomDirection === 'in') {
this.$scope.xAxis.set('displayRange', {
min: currentXaxis.min + xAxisDist,
max: currentXaxis.max - xAxisDist
});
this.$scope.yAxis.set('displayRange', {
min: currentYaxis.min + yAxisDist,
max: currentYaxis.max - yAxisDist
});
} else if (zoomDirection === 'out') {
this.$scope.xAxis.set('displayRange', {
min: currentXaxis.min - xAxisDist,
max: currentXaxis.max + xAxisDist
});
this.$scope.yAxis.set('displayRange', {
min: currentYaxis.min - yAxisDist,
max: currentYaxis.max + yAxisDist
});
}
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.wheelZoom = function (event) {
const ZOOM_AMT = 0.1;
event.preventDefault();
if (!this.positionOverPlot) {
return;
}
const isPinchToZoom = event.ctrlKey === true;
let isZoomIn = event.wheelDelta < 0;
let isZoomOut = event.wheelDelta >= 0;
//Flip the zoom direction if this is pinch to zoom
if (isPinchToZoom) {
if (isZoomIn === true) {
isZoomOut = true;
isZoomIn = false;
} else if (isZoomOut === true) {
isZoomIn = true;
isZoomOut = false;
}
}
let xDisplayRange = this.$scope.xAxis.get('displayRange');
let yDisplayRange = this.$scope.yAxis.get('displayRange');
// when there is no plot data, the ranges can be undefined
// in which case we should not perform zoom
if (!xDisplayRange || !yDisplayRange) {
return;
}
this.freeze();
window.clearTimeout(this.stillZooming);
let xAxisDist = (xDisplayRange.max - xDisplayRange.min);
let yAxisDist = (yDisplayRange.max - yDisplayRange.min);
let xDistMouseToMax = xDisplayRange.max - this.positionOverPlot.x;
let xDistMouseToMin = this.positionOverPlot.x - xDisplayRange.min;
let yDistMouseToMax = yDisplayRange.max - this.positionOverPlot.y;
let yDistMouseToMin = this.positionOverPlot.y - yDisplayRange.min;
let xAxisMaxDist = xDistMouseToMax / xAxisDist;
let xAxisMinDist = xDistMouseToMin / xAxisDist;
let yAxisMaxDist = yDistMouseToMax / yAxisDist;
let yAxisMinDist = yDistMouseToMin / yAxisDist;
let plotHistoryStep;
if (!plotHistoryStep) {
plotHistoryStep = {
x: xDisplayRange,
y: yDisplayRange
};
}
if (isZoomIn) {
this.$scope.xAxis.set('displayRange', {
min: xDisplayRange.min + ((xAxisDist * ZOOM_AMT) * xAxisMinDist),
max: xDisplayRange.max - ((xAxisDist * ZOOM_AMT) * xAxisMaxDist)
});
this.$scope.yAxis.set('displayRange', {
min: yDisplayRange.min + ((yAxisDist * ZOOM_AMT) * yAxisMinDist),
max: yDisplayRange.max - ((yAxisDist * ZOOM_AMT) * yAxisMaxDist)
});
} else if (isZoomOut) {
this.$scope.xAxis.set('displayRange', {
min: xDisplayRange.min - ((xAxisDist * ZOOM_AMT) * xAxisMinDist),
max: xDisplayRange.max + ((xAxisDist * ZOOM_AMT) * xAxisMaxDist)
});
this.$scope.yAxis.set('displayRange', {
min: yDisplayRange.min - ((yAxisDist * ZOOM_AMT) * yAxisMinDist),
max: yDisplayRange.max + ((yAxisDist * ZOOM_AMT) * yAxisMaxDist)
});
}
this.stillZooming = window.setTimeout(function () {
this.plotHistory.push(plotHistoryStep);
plotHistoryStep = undefined;
this.$scope.$emit('user:viewport:change:end');
}.bind(this), 250);
};
MCTPlotController.prototype.startPan = function ($event) {
this.$canvas.addClass('plot-drag');
this.$canvas.removeClass('plot-marquee');
this.trackMousePosition($event);
this.freeze();
this.pan = {
start: this.positionOverPlot
};
$event.preventDefault();
this.trackHistory();
return false;
};
MCTPlotController.prototype.updatePan = function () {
// calculate offset between points. Apply that offset to viewport.
if (!this.pan) {
return;
}
const dX = this.pan.start.x - this.positionOverPlot.x;
const dY = this.pan.start.y - this.positionOverPlot.y;
const xRange = this.config.xAxis.get('displayRange');
const yRange = this.config.yAxis.get('displayRange');
this.config.xAxis.set('displayRange', {
min: xRange.min + dX,
max: xRange.max + dX
});
this.config.yAxis.set('displayRange', {
min: yRange.min + dY,
max: yRange.max + dY
});
};
MCTPlotController.prototype.trackHistory = function () {
this.plotHistory.push({
x: this.config.xAxis.get('displayRange'),
y: this.config.yAxis.get('displayRange')
});
};
MCTPlotController.prototype.endPan = function () {
this.pan = undefined;
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.freeze = function () {
this.config.yAxis.set('frozen', true);
this.config.xAxis.set('frozen', true);
};
MCTPlotController.prototype.clear = function () {
this.config.yAxis.set('frozen', false);
this.config.xAxis.set('frozen', false);
this.$scope.plotHistory = this.plotHistory = [];
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.back = function () {
const previousAxisRanges = this.plotHistory.pop();
if (this.plotHistory.length === 0) {
this.clear();
return;
}
this.config.xAxis.set('displayRange', previousAxisRanges.x);
this.config.yAxis.set('displayRange', previousAxisRanges.y);
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.destroy = function () {
this.stopListening();
};
MCTPlotController.prototype.toggleCursorGuide = function ($event) {
this.cursorGuide = !this.cursorGuide;
};
MCTPlotController.prototype.toggleGridLines = function ($event) {
this.gridLines = !this.gridLines;
};
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];
if (yAxisObject) {
series.emit('change:yKey', yAxisObject.key);
this.config.yAxis.set('label', label);
this.$scope.yAxisLabel = label;
}
};
return MCTPlotController;
});

View File

@ -1,46 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./MCTPlotController',
'../../res/templates/mct-plot.html'
], function (
MCTPlotController,
PlotTemplate
) {
function MCTPlot() {
return {
restrict: "E",
template: PlotTemplate,
controller: MCTPlotController,
controllerAs: 'mctPlotController',
bindToController: {
config: "="
},
scope: true
};
}
return MCTPlot;
});

View File

@ -1,279 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'lodash',
'../lib/eventHelpers'
], function (
_,
eventHelpers
) {
const e10 = Math.sqrt(50);
const e5 = Math.sqrt(10);
const e2 = Math.sqrt(2);
/**
* Nicely formatted tick steps from d3-array.
*/
function tickStep(start, stop, count) {
const step0 = Math.abs(stop - start) / Math.max(0, count);
let step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10));
const error = step0 / step1;
if (error >= e10) {
step1 *= 10;
} else if (error >= e5) {
step1 *= 5;
} else if (error >= e2) {
step1 *= 2;
}
return stop < start ? -step1 : step1;
}
/**
* Find the precision (number of decimals) of a step. Used to round
* ticks to precise values.
*/
function getPrecision(step) {
const exponential = step.toExponential();
const i = exponential.indexOf('e');
if (i === -1) {
return 0;
}
let precision = Math.max(0, -(Number(exponential.slice(i + 1))));
if (precision > 20) {
precision = 20;
}
return precision;
}
/**
* Linear tick generation from d3-array.
*/
function ticks(start, stop, count) {
const step = tickStep(start, stop, count);
const precision = getPrecision(step);
return _.range(
Math.ceil(start / step) * step,
Math.floor(stop / step) * step + step / 2, // inclusive
step
).map(function round(tick) {
return Number(tick.toFixed(precision));
});
}
function commonPrefix(a, b) {
const maxLen = Math.min(a.length, b.length);
let breakpoint = 0;
for (let i = 0; i < maxLen; i++) {
if (a[i] !== b[i]) {
break;
}
if (a[i] === ' ') {
breakpoint = i + 1;
}
}
return a.slice(0, breakpoint);
}
function commonSuffix(a, b) {
const maxLen = Math.min(a.length, b.length);
let breakpoint = 0;
for (let i = 0; i <= maxLen; i++) {
if (a[a.length - i] !== b[b.length - i]) {
break;
}
if ('. '.indexOf(a[a.length - i]) !== -1) {
breakpoint = i;
}
}
return a.slice(a.length - breakpoint);
}
function MCTTicksController($scope, $element) {
this.$onInit = () => {
this.$scope = $scope;
this.$element = $element;
this.tickCount = 4;
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();
};
}
MCTTicksController.$inject = ['$scope', '$element'];
eventHelpers.extend(MCTTicksController.prototype);
/**
* Determine whether ticks should be regenerated for a given range.
* 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, forceRegeneration) {
if (forceRegeneration) {
return true;
}
if (!this.tickRange || !this.$scope.ticks || !this.$scope.ticks.length) {
return true;
}
if (this.tickRange.max > range.max || this.tickRange.min < range.min) {
return true;
}
if (Math.abs(range.max - this.tickRange.max) > this.tickRange.step) {
return true;
}
if (Math.abs(this.tickRange.min - range.min) > this.tickRange.step) {
return true;
}
return false;
};
MCTTicksController.prototype.getTicks = function () {
const number = this.tickCount;
const clampRange = this.axis.get('values');
const range = this.axis.get('displayRange');
if (clampRange) {
return clampRange.filter(function (value) {
return value <= range.max && value >= range.min;
}, this);
}
return ticks(range.min, range.max, number);
};
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;
delete this.$scope.max;
delete this.$scope.interval;
delete this.tickRange;
delete this.$scope.ticks;
delete this.shouldCheckWidth;
return;
}
const format = this.axis.get('format');
if (!format) {
return;
}
this.$scope.min = range.min;
this.$scope.max = range.max;
this.$scope.interval = Math.abs(range.min - range.max);
if (this.shouldRegenerateTicks(range, forceRegeneration)) {
let newTicks = this.getTicks();
this.tickRange = {
min: Math.min.apply(Math, newTicks),
max: Math.max.apply(Math, newTicks),
step: newTicks[1] - newTicks[0]
};
newTicks = newTicks
.map(function (tickValue) {
return {
value: tickValue,
text: format(tickValue)
};
}, this);
if (newTicks.length && typeof newTicks[0].text === 'string') {
const tickText = newTicks.map(function (t) {
return t.text;
});
const prefix = tickText.reduce(commonPrefix);
const suffix = tickText.reduce(commonSuffix);
newTicks.forEach(function (t, i) {
t.fullText = t.text;
if (suffix.length) {
t.text = t.text.slice(prefix.length, -suffix.length);
} else {
t.text = t.text.slice(prefix.length);
}
});
}
this.$scope.ticks = newTicks;
this.shouldCheckWidth = true;
}
this.scheduleTickUpdate();
};
MCTTicksController.prototype.scheduleTickUpdate = function () {
if (this.tickUpdate) {
return;
}
this.tickUpdate = true;
setTimeout(this.doTickUpdate.bind(this), 0);
};
MCTTicksController.prototype.doTickUpdate = function () {
if (this.shouldCheckWidth) {
this.$scope.$digest();
const element = this.$element[0];
const tickElements = element.querySelectorAll('.gl-plot-tick > span');
const tickWidth = Number([].reduce.call(tickElements, function (memo, first) {
return Math.max(memo, first.offsetWidth);
}, 0));
this.$scope.tickWidth = tickWidth;
this.$scope.$emit('plot:tickWidth', tickWidth);
this.shouldCheckWidth = false;
}
this.$scope.$digest();
this.tickUpdate = false;
};
return MCTTicksController;
});

View File

@ -1,43 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'./MCTTicksController'
], function (
MCTTicksController
) {
function MCTTicksDirective() {
return {
priority: 1000,
restrict: "E",
scope: true,
controllerAs: 'ticksController',
controller: MCTTicksController,
bindToController: {
axis: '='
}
};
}
return MCTTicksDirective;
});

View File

@ -1,37 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([
'../../res/templates/plot.html'
], function (
PlotTemplate
) {
return function MCTOverlayPlot() {
return {
restrict: "E",
template: PlotTemplate,
scope: {
domainObject: "="
}
};
};
});

View File

@ -1,342 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
/*jscs:disable disallowDanglingUnderscores */
define([
'lodash',
'../configuration/PlotConfigurationModel',
'../configuration/configStore',
'../lib/eventHelpers'
], function (
_,
PlotConfigurationModel,
configStore,
eventHelpers
) {
/**
TODO: Need to separate off plot configuration and specifying of defaults,
is part of onDomainObjectChange as it can be triggered by mutation.
*/
/**
* Controller for a plot.
*
* @constructor.
*/
function PlotController(
$scope,
$element,
formatService,
openmct,
objectService,
exportImageService
) {
this.$scope = $scope;
this.$element = $element;
this.formatService = formatService;
this.openmct = openmct;
this.objectService = objectService;
this.exportImageService = exportImageService;
this.cursorGuide = false;
this.gridLines = true;
$scope.pending = 0;
this.clearData = this.clearData.bind(this);
this.listenTo($scope, 'user:viewport:change:end', this.onUserViewportChangeEnd, this);
this.listenTo($scope, '$destroy', this.destroy, this);
this.listenTo($scope, 'clearData', this.clearData);
this.config = this.getConfig(this.$scope.domainObject);
this.listenTo(this.config.series, 'add', this.addSeries, this);
this.listenTo(this.config.series, 'remove', this.removeSeries, this);
this.config.series.forEach(this.addSeries, this);
this.followTimeConductor();
this.newStyleDomainObject = $scope.domainObject.useCapability('adapter');
this.keyString = this.openmct.objects.makeKeyString(this.newStyleDomainObject.identifier);
this.filterObserver = this.openmct.objects.observe(
this.newStyleDomainObject,
'configuration.filters',
this.updateFiltersAndResubscribe.bind(this)
);
}
eventHelpers.extend(PlotController.prototype);
PlotController.prototype.followTimeConductor = function () {
this.listenTo(this.openmct.time, 'bounds', this.updateDisplayBounds, this);
this.listenTo(this.openmct.time, 'timeSystem', this.syncXAxisToTimeSystem, this);
this.synchronized(true);
};
PlotController.prototype.loadSeriesData = function (series) {
if (this.$element[0].offsetWidth === 0) {
this.scheduleLoad(series);
return;
}
this.startLoading();
const options = {
size: this.$element[0].offsetWidth,
domain: this.config.xAxis.get('key')
};
series.load(options)
.then(this.stopLoading.bind(this));
};
PlotController.prototype.scheduleLoad = function (series) {
if (!this.scheduledLoads) {
this.startLoading();
this.scheduledLoads = [];
this.checkForSize = setInterval(function () {
if (this.$element[0].offsetWidth === 0) {
return;
}
this.stopLoading();
this.scheduledLoads.forEach(this.loadSeriesData, this);
delete this.scheduledLoads;
clearInterval(this.checkForSize);
delete this.checkForSize;
}.bind(this));
}
if (this.scheduledLoads.indexOf(series) === -1) {
this.scheduledLoads.push(series);
}
};
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);
this.listenTo(series, 'change:interpolate', () => {
this.loadSeriesData(series);
}, this);
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);
};
PlotController.prototype.getConfig = function (domainObject) {
const configId = domainObject.getId();
let config = configStore.get(configId);
if (!config) {
const newDomainObject = domainObject.useCapability('adapter');
config = new PlotConfigurationModel({
id: configId,
domainObject: newDomainObject,
openmct: this.openmct
});
configStore.add(configId, config);
}
return config;
};
PlotController.prototype.syncXAxisToTimeSystem = function (timeSystem) {
this.config.xAxis.set('key', timeSystem.key);
this.config.xAxis.emit('resetSeries');
};
PlotController.prototype.destroy = function () {
configStore.deleteStore(this.config.id);
this.stopListening();
if (this.checkForSize) {
clearInterval(this.checkForSize);
delete this.checkForSize;
}
if (this.filterObserver) {
this.filterObserver();
}
};
PlotController.prototype.loadMoreData = function (range, purge) {
this.config.series.forEach(plotSeries => {
this.startLoading();
plotSeries.load({
size: this.$element[0].offsetWidth,
start: range.min,
end: range.max,
domain: this.config.xAxis.get('key')
})
.then(this.stopLoading());
if (purge) {
plotSeries.purgeRecordsOutsideRange(range);
}
});
};
/**
* 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;
this.$scope.$broadcast('plot:clearHistory');
this.skipReloadOnInteraction = false;
this.loadMoreData(newRange, true);
} else {
// Drop any data that is more than 1x (max-min) before min.
// Limit these purges to once a second.
if (!this.nextPurge || this.nextPurge < Date.now()) {
const keepRange = {
min: newRange.min - (newRange.max - newRange.min),
max: newRange.max
};
this.config.series.forEach(function (series) {
series.purgeRecordsOutsideRange(keepRange);
});
this.nextPurge = Date.now() + 1000;
}
}
};
PlotController.prototype.startLoading = function () {
this.$scope.pending += 1;
};
PlotController.prototype.stopLoading = function () {
this.$scope.$evalAsync(() => {
this.$scope.pending -= 1;
});
};
/**
* Getter/setter for "synchronized" value. If not synchronized and
* time conductor is in clock mode, will mark objects as unsynced so that
* displays can update accordingly.
* @private
*/
PlotController.prototype.synchronized = function (value) {
if (typeof value !== 'undefined') {
this._synchronized = value;
const isUnsynced = !value && this.openmct.time.clock();
if (this.$scope.domainObject.getCapability('status')) {
this.$scope.domainObject.getCapability('status')
.set('timeconductor-unsynced', isUnsynced);
}
}
return this._synchronized;
};
/**
* Handle end of user viewport change: load more data for current display
* bounds, and mark view as synchronized if bounds match configured bounds.
* @private
*/
PlotController.prototype.onUserViewportChangeEnd = function () {
const xDisplayRange = this.config.xAxis.get('displayRange');
const xRange = this.config.xAxis.get('range');
if (!this.skipReloadOnInteraction) {
this.loadMoreData(xDisplayRange);
}
this.synchronized(xRange.min === xDisplayRange.min
&& xRange.max === xDisplayRange.max);
};
PlotController.prototype.updateFiltersAndResubscribe = function (updatedFilters) {
this.config.series.forEach(function (series) {
series.updateFiltersAndRefresh(updatedFilters[series.keyString]);
});
};
PlotController.prototype.clearData = function () {
this.config.series.forEach(function (series) {
series.reset();
});
};
/**
* Export view as JPG.
*/
PlotController.prototype.exportJPG = function () {
const plotElement = this.$element.children()[1];
this.exportImageService.exportJPG(plotElement, 'plot.jpg', 'export-plot');
};
/**
* Export view as PNG.
*/
PlotController.prototype.exportPNG = function () {
const plotElement = this.$element.children()[1];
this.exportImageService.exportPNG(plotElement, 'plot.png', 'export-plot');
};
PlotController.prototype.toggleCursorGuide = function ($event) {
this.cursorGuide = !this.cursorGuide;
this.$scope.$broadcast('cursorguide', $event);
};
PlotController.prototype.toggleGridLines = function ($event) {
this.gridLines = !this.gridLines;
this.$scope.$broadcast('toggleGridLines', $event);
};
return PlotController;
});

View File

@ -1,167 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2021, 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.
*****************************************************************************/
define([], function () {
function StackedPlotController($scope, openmct, objectService, $element, exportImageService) {
let tickWidth = 0;
let composition;
let currentRequest;
let unlisten;
let tickWidthMap = {};
this.$element = $element;
this.exportImageService = exportImageService;
this.$scope = $scope;
this.cursorGuide = false;
$scope.telemetryObjects = [];
function onDomainObjectChange(domainObject) {
const thisRequest = {
pending: 0
};
const telemetryObjects = $scope.telemetryObjects = [];
const thisTickWidthMap = {};
currentRequest = thisRequest;
$scope.currentRequest = thisRequest;
tickWidthMap = thisTickWidthMap;
if (unlisten) {
unlisten();
unlisten = undefined;
}
function addChild(child) {
const id = openmct.objects.makeKeyString(child.identifier);
const legacyObject = openmct.legacyObject(child);
thisTickWidthMap[id] = 0;
telemetryObjects.push(legacyObject);
}
function removeChild(childIdentifier) {
const id = openmct.objects.makeKeyString(childIdentifier);
delete thisTickWidthMap[id];
const childObj = telemetryObjects.filter(function (c) {
return c.getId() === id;
})[0];
if (childObj) {
const index = telemetryObjects.indexOf(childObj);
telemetryObjects.splice(index, 1);
$scope.$broadcast('plot:tickWidth', Math.max(...Object.values(tickWidthMap)));
}
}
function compositionReorder(reorderPlan) {
let oldComposition = telemetryObjects.slice();
reorderPlan.forEach((reorder) => {
telemetryObjects[reorder.newIndex] = oldComposition[reorder.oldIndex];
});
}
thisRequest.pending += 1;
openmct.objects.get(domainObject.getId())
.then(function (obj) {
thisRequest.pending -= 1;
if (thisRequest !== currentRequest) {
return;
}
composition = openmct.composition.get(obj);
composition.on('add', addChild);
composition.on('remove', removeChild);
composition.on('reorder', compositionReorder);
composition.load();
unlisten = function () {
composition.off('add', addChild);
composition.off('remove', removeChild);
composition.off('reorder', compositionReorder);
};
});
}
function onCompositionChange(newComp, oldComp) {
if (newComp !== oldComp) {
$scope.telemetryObjects = [];
objectService.getObjects(newComp).then(function (objects) {
newComp.forEach(function (id) {
$scope.telemetryObjects.push(objects[id]);
});
});
}
}
$scope.$watch('domainObject', onDomainObjectChange);
$scope.$watch('domainObject.getModel().composition', onCompositionChange);
$scope.$on('plot:tickWidth', function ($e, width) {
const plotId = $e.targetScope.domainObject.getId();
if (!Object.prototype.hasOwnProperty.call(tickWidthMap, plotId)) {
return;
}
tickWidthMap[plotId] = Math.max(width, tickWidthMap[plotId]);
const newTickWidth = Math.max(...Object.values(tickWidthMap));
if (newTickWidth !== tickWidth || width !== tickWidth) {
tickWidth = newTickWidth;
$scope.$broadcast('plot:tickWidth', tickWidth);
}
});
$scope.$on('plot:highlight:update', function ($e, point) {
$scope.$broadcast('plot:highlight:set', point);
});
}
StackedPlotController.prototype.exportJPG = function () {
this.hideExportButtons = true;
this.exportImageService.exportJPG(this.$element[0], 'stacked-plot.jpg', 'export-plot')
.finally(function () {
this.hideExportButtons = false;
}.bind(this));
};
StackedPlotController.prototype.exportPNG = function () {
this.hideExportButtons = true;
this.exportImageService.exportPNG(this.$element[0], 'stacked-plot.png', 'export-plot')
.finally(function () {
this.hideExportButtons = false;
}.bind(this));
};
StackedPlotController.prototype.toggleCursorGuide = function ($event) {
this.cursorGuide = !this.cursorGuide;
this.$scope.$broadcast('cursorguide', $event);
};
StackedPlotController.prototype.toggleGridLines = function ($event) {
this.gridLines = !this.gridLines;
this.$scope.$broadcast('toggleGridLines', $event);
};
return StackedPlotController;
});

Some files were not shown because too many files have changed in this diff Show More