diff --git a/example/generator/GeneratorProvider.js b/example/generator/GeneratorProvider.js
index d053c68f7c..1ca0b75155 100644
--- a/example/generator/GeneratorProvider.js
+++ b/example/generator/GeneratorProvider.js
@@ -30,7 +30,8 @@ define([
amplitude: 1,
period: 10,
offset: 0,
- dataRateInHz: 1
+ dataRateInHz: 1,
+ phase: 0
};
function GeneratorProvider() {
@@ -50,9 +51,12 @@ define([
'amplitude',
'period',
'offset',
- 'dataRateInHz'
+ 'dataRateInHz',
+ 'phase',
];
+ request = request || {};
+
var workerRequest = {};
props.forEach(function (prop) {
@@ -67,7 +71,7 @@ define([
}
workerRequest[prop] = Number(workerRequest[prop]);
});
-
+ workerRequest.name = domainObject.name;
return workerRequest;
};
diff --git a/example/generator/StateGeneratorProvider.js b/example/generator/StateGeneratorProvider.js
new file mode 100644
index 0000000000..7784bb10cc
--- /dev/null
+++ b/example/generator/StateGeneratorProvider.js
@@ -0,0 +1,80 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2017, 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 StateGeneratorProvider() {
+
+ }
+
+ function pointForTimestamp(timestamp, duration, name) {
+ return {
+ name: name,
+ utc: Math.floor(timestamp / duration) * duration,
+ value: Math.floor(timestamp / duration) % 2
+ };
+ }
+
+ StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
+ return domainObject.type === 'example.state-generator';
+ };
+
+ StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
+ var duration = domainObject.telemetry.duration * 1000;
+
+ var interval = setInterval(function () {
+ var now = Date.now();
+ callback(pointForTimestamp(now, duration, domainObject.name));
+ }, duration);
+
+ return function () {
+ clearInterval(interval);
+ };
+ };
+
+
+ StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
+ return domainObject.type === 'example.state-generator';
+ };
+
+ StateGeneratorProvider.prototype.request = function (domainObject, options) {
+ var start = options.start;
+ var end = options.end;
+ var duration = domainObject.telemetry.duration * 1000;
+ if (options.strategy === 'latest' || options.size === 1) {
+ start = end;
+ }
+ var data = [];
+ while (start <= end && data.length < 5000) {
+ data.push(pointForTimestamp(start, duration, domainObject.name));
+ start += 5000;
+ }
+ return Promise.resolve(data);
+ };
+
+ return StateGeneratorProvider;
+
+});
diff --git a/example/generator/generatorWorker.js b/example/generator/generatorWorker.js
index 5b45363b1d..32e9e4c5e1 100644
--- a/example/generator/generatorWorker.js
+++ b/example/generator/generatorWorker.js
@@ -62,10 +62,11 @@
self.postMessage({
id: message.id,
data: {
+ name: data.name,
utc: nextStep,
yesterday: nextStep - 60*60*24*1000,
- sin: sin(nextStep, data.period, data.amplitude, data.offset),
- cos: cos(nextStep, data.period, data.amplitude, data.offset)
+ sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase),
+ cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase)
}
});
nextStep += step;
@@ -82,21 +83,22 @@
}
function onRequest(message) {
- var data = message.data;
- if (data.end == undefined) {
- data.end = Date.now();
+ var request = message.data;
+ if (request.end == undefined) {
+ request.end = Date.now();
}
- if (data.start == undefined){
- data.start = data.end - FIFTEEN_MINUTES;
+ if (request.start == undefined){
+ request.start = request.end - FIFTEEN_MINUTES;
}
var now = Date.now();
- var start = data.start;
- var end = data.end > now ? now : data.end;
- var amplitude = data.amplitude;
- var period = data.period;
- var offset = data.offset;
- var dataRateInHz = data.dataRateInHz;
+ var start = request.start;
+ var end = request.end > now ? now : request.end;
+ var amplitude = request.amplitude;
+ var period = request.period;
+ var offset = request.offset;
+ var dataRateInHz = request.dataRateInHz;
+ var phase = request.phase;
var step = 1000 / dataRateInHz;
var nextStep = start - (start % step) + step;
@@ -105,10 +107,11 @@
for (; nextStep < end && data.length < 5000; nextStep += step) {
data.push({
+ name: request.name,
utc: nextStep,
yesterday: nextStep - 60*60*24*1000,
- sin: sin(nextStep, period, amplitude, offset),
- cos: cos(nextStep, period, amplitude, offset)
+ sin: sin(nextStep, period, amplitude, offset, phase),
+ cos: cos(nextStep, period, amplitude, offset, phase)
});
}
self.postMessage({
@@ -117,14 +120,14 @@
});
}
- function cos(timestamp, period, amplitude, offset) {
+ function cos(timestamp, period, amplitude, offset, phase) {
return amplitude *
- Math.cos(timestamp / period / 1000 * Math.PI * 2) + offset;
+ Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
}
- function sin(timestamp, period, amplitude, offset) {
+ function sin(timestamp, period, amplitude, offset, phase) {
return amplitude *
- Math.sin(timestamp / period / 1000 * Math.PI * 2) + offset;
+ Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
}
function sendError(error, message) {
diff --git a/example/generator/plugin.js b/example/generator/plugin.js
index 6a99448f5a..1039e61039 100644
--- a/example/generator/plugin.js
+++ b/example/generator/plugin.js
@@ -23,10 +23,12 @@
define([
"./GeneratorProvider",
- "./SinewaveLimitCapability"
+ "./SinewaveLimitCapability",
+ "./StateGeneratorProvider"
], function (
GeneratorProvider,
- SinewaveLimitCapability
+ SinewaveLimitCapability,
+ StateGeneratorProvider
) {
var legacyExtensions = {
@@ -46,6 +48,75 @@ define([
openmct.legacyExtension(type, extension)
})
});
+
+ openmct.types.addType("example.state-generator", {
+ name: "State Generator",
+ description: "For development use. Generates test enumerated telemetry by cycling through a given set of states",
+ cssClass: "icon-telemetry",
+ creatable: true,
+ form: [
+ {
+ name: "State Duration (seconds)",
+ control: "textfield",
+ cssClass: "l-input-sm l-numeric",
+ key: "duration",
+ required: true,
+ property: [
+ "telemetry",
+ "duration"
+ ],
+ pattern: "^\\d*(\\.\\d*)?$"
+ }
+ ],
+ initialize: function (object) {
+ object.telemetry = {
+ duration: 5,
+ values: [
+ {
+ key: "name",
+ name: "Name"
+ },
+ {
+ key: "utc",
+ name: "Time",
+ format: "utc",
+ hints: {
+ domain: 1
+ }
+ },
+ {
+ key: "state",
+ source: "value",
+ name: "State",
+ format: "enum",
+ enumerations: [
+ {
+ value: 0,
+ string: "OFF"
+ },
+ {
+ value: 1,
+ string: "ON"
+ }
+ ],
+ hints: {
+ range: 1
+ }
+ },
+ {
+ key: "value",
+ name: "Value",
+ hints: {
+ range: 2
+ }
+ }
+ ]
+ }
+ }
+ });
+
+ openmct.telemetry.addProvider(new StateGeneratorProvider());
+
openmct.types.addType("generator", {
name: "Sine Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
@@ -99,6 +170,18 @@ define([
"dataRateInHz"
],
pattern: "^\\d*(\\.\\d*)?$"
+ },
+ {
+ name: "Phase (radians)",
+ control: "textfield",
+ cssClass: "l-input-sm l-numeric",
+ key: "phase",
+ required: true,
+ property: [
+ "telemetry",
+ "phase"
+ ],
+ pattern: "^\\d*(\\.\\d*)?$"
}
],
initialize: function (object) {
@@ -107,7 +190,12 @@ define([
amplitude: 1,
offset: 0,
dataRateInHz: 1,
+ phase: 0,
values: [
+ {
+ key: "name",
+ name: "Name"
+ },
{
key: "utc",
name: "Time",
@@ -142,6 +230,7 @@ define([
};
}
});
+
openmct.telemetry.addProvider(new GeneratorProvider());
};
diff --git a/example/imagery/plugin.js b/example/imagery/plugin.js
index 5e30ebfc85..21d2cd7c2c 100644
--- a/example/imagery/plugin.js
+++ b/example/imagery/plugin.js
@@ -48,8 +48,9 @@ define([
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
];
- function pointForTimestamp(timestamp) {
+ function pointForTimestamp(timestamp, name) {
return {
+ name: name,
utc: Math.floor(timestamp / 5000) * 5000,
url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
};
@@ -61,7 +62,7 @@ define([
},
subscribe: function (domainObject, callback) {
var interval = setInterval(function () {
- callback(pointForTimestamp(Date.now()));
+ callback(pointForTimestamp(Date.now(), domainObject.name));
}, 5000);
return function (interval) {
@@ -79,8 +80,8 @@ define([
var start = options.start;
var end = options.end;
var data = [];
- while (start < end && data.length < 5000) {
- data.push(pointForTimestamp(start));
+ while (start <= end && data.length < 5000) {
+ data.push(pointForTimestamp(start, domainObject.name));
start += 5000;
}
return Promise.resolve(data);
@@ -93,7 +94,7 @@ define([
options.strategy === 'latest';
},
request: function (domainObject, options) {
- return Promise.resolve([pointForTimestamp(Date.now())]);
+ return Promise.resolve([pointForTimestamp(Date.now(), domainObject.name)]);
}
};
@@ -109,6 +110,10 @@ define([
initialize: function (object) {
object.telemetry = {
values: [
+ {
+ name: 'Name',
+ key: 'name'
+ },
{
name: 'Time',
key: 'utc',
diff --git a/example/styleguide/res/sass/_style-guide-base.scss b/example/styleguide/res/sass/_style-guide-base.scss
index 5c6cbd56c4..f64681d876 100644
--- a/example/styleguide/res/sass/_style-guide-base.scss
+++ b/example/styleguide/res/sass/_style-guide-base.scss
@@ -58,11 +58,7 @@
position: relative;
}
- .w-mct-example {
- div {
- margin-bottom: $interiorMarginLg;
- }
- }
+ .w-mct-example > div { margin-bottom: $interiorMarginLg; }
code,
pre {
diff --git a/karma.conf.js b/karma.conf.js
index 533ebbff3d..486ee66010 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -89,7 +89,8 @@ module.exports = function(config) {
"dist/reports/coverage",
check: {
global: {
- lines: 80
+ lines: 80,
+ excludes: ['src/plugins/plot/**/*.js']
}
}
},
diff --git a/openmct.js b/openmct.js
index 0dd3a54e2a..645e005201 100644
--- a/openmct.js
+++ b/openmct.js
@@ -101,6 +101,7 @@ define([
var openmct = new MCT();
openmct.legacyRegistry = defaultRegistry;
+ openmct.install(openmct.plugins.Plot());
if (typeof BUILD_CONSTANTS !== 'undefined') {
openmct.install(buildInfo(BUILD_CONSTANTS));
diff --git a/platform/commonUI/general/res/sass/_constants.scss b/platform/commonUI/general/res/sass/_constants.scss
index 7ab95e48bd..a50dadb31b 100644
--- a/platform/commonUI/general/res/sass/_constants.scss
+++ b/platform/commonUI/general/res/sass/_constants.scss
@@ -99,7 +99,7 @@ $plotXBarH: 32px;
$plotLegendH: 20px;
$plotSwatchD: 8px;
// 1: Top, 2: right, 3: bottom, 4: left
-$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH, $plotYBarW);
+$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW);
/* min plot height is based on user testing to find minimum useful height */
$plotMinH: 95px;
/*************** Bubbles */
diff --git a/platform/commonUI/general/res/sass/_global.scss b/platform/commonUI/general/res/sass/_global.scss
index dc4b9ee735..8bcfeab8af 100644
--- a/platform/commonUI/general/res/sass/_global.scss
+++ b/platform/commonUI/general/res/sass/_global.scss
@@ -40,7 +40,7 @@
* Use https://icomoon.io/app with icomoon-project-openmct-symbols-12px.json
* to generate font files
*/
- font-family: 'symbolsfont 12px';
+ font-family: 'symbolsfont-12px';
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot');
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot?#iefix') format('embedded-opentype'),
url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.woff') format('woff'),
@@ -248,6 +248,12 @@ a.disabled {
color: rgba(#fff, 0.2);
}
+.comma-list span {
+ &:not(:first-child) {
+ &:before { content: ', '; }
+ }
+}
+
.test-stripes {
@include bgDiagonalStripes();
}
diff --git a/platform/commonUI/general/res/sass/_icons.scss b/platform/commonUI/general/res/sass/_icons.scss
index 0bb55c6744..e7326ba606 100644
--- a/platform/commonUI/general/res/sass/_icons.scss
+++ b/platform/commonUI/general/res/sass/_icons.scss
@@ -44,6 +44,12 @@
}
}
+
+.t-alert-unsynced {
+ @extend .icon-alert-triangle;
+ color: $colorPausedBg;
+}
+
.bar .ui-symbol {
display: inline-block;
}
@@ -81,18 +87,5 @@
@include transform(scale(0.3));
z-index: 2;
}
-
-/* .t-item-icon-glyph {
- &:after {
- color: $colorIconLink;
- content: '\e921'; //$glyph-icon-link;
- height: auto; width: auto;
- position: absolute;
- left: 0; top: 0; right: 0; bottom: 20%;
- @include transform-origin(bottom left);
- @include transform(scale(0.3));
- z-index: 2;
- }
- }*/
}
}
diff --git a/platform/commonUI/general/res/sass/_inspector.scss b/platform/commonUI/general/res/sass/_inspector.scss
index 79644a78b7..1e9e8d8e26 100644
--- a/platform/commonUI/general/res/sass/_inspector.scss
+++ b/platform/commonUI/general/res/sass/_inspector.scss
@@ -53,6 +53,7 @@
.l-inspector-part {
box-sizing: border-box;
padding-right: $interiorMargin;
+
.tree .form {
margin-left: $treeVCW + $interiorMarginLg;
}
@@ -78,6 +79,7 @@
}
}
.form-row {
+ // To be replaced with .inspector-config, see below.
@include align-items(center);
border: none !important;
margin-bottom: 0 !important;
@@ -99,15 +101,12 @@
position: relative;
}
- ul li {
- margin-bottom: $interiorMarginLg;
- }
-
em.t-inspector-part-header {
border-radius: $basicCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
- margin-bottom: $interiorMargin;
+ margin-top: $interiorMarginLg;
+ //margin-bottom: $interiorMargin;
padding: floor($formTBPad * .75) $formLRPad;
text-transform: uppercase;
}
@@ -201,3 +200,102 @@ mct-representation:not(.s-status-editing) .l-inspect {
pointer-events: inherit;
}
}
+
+// NEW COMPACT FORM, FOR USE IN INSPECTOR
+// ul > li > label, control
+// Make a new UL for each form section
+// Allow control-first, controls-below
+
+.l-inspect .tree ul li,
+.inspector-config ul li {
+ padding: 2px 0;
+}
+
+
+.inspector-config {
+ $labelW: 40%;
+ $minW: $labelW;
+ ul {
+ margin-bottom: $interiorMarginLg;
+ li {
+ @include display(flex);
+ @include flex-wrap(wrap);
+ @include align-items(center);
+ label,
+ .control {
+ @include display(flex);
+ min-width: $minW;
+ }
+ label {
+ line-height: inherit;
+ padding: $interiorMarginSm 0;
+ width: $labelW;
+ }
+ .control {
+ @include flex-grow(1);
+ }
+
+ &:not(.section-header) {
+ &:not(.connects-to-previous) {
+ //border-top: 1px solid $colorFormLines;
+ }
+ }
+
+ &.connects-to-previous {
+ padding-top: 0 !important;
+ }
+
+ &.section-header {
+ margin-top: $interiorMarginLg;
+ border-top: 1px solid $colorFormLines;
+ }
+
+ &.controls-first {
+ .control {
+ @include flex-grow(0);
+ margin-right: $interiorMargin;
+ min-width: 0;
+ order: 1;
+ width: auto;
+ }
+ label {
+ @include flex-grow(1);
+ order: 2;
+ width: auto;
+ }
+ }
+ &.controls-under {
+ display: block;
+ .control, label {
+ display: block;
+ width: auto;
+ }
+
+ ul li {
+ border-top: none !important;
+ padding: 0;
+ }
+ }
+ }
+ }
+
+ .form-error {
+ // Block element that visually flags an error and contains a message
+ background-color: $colorFormFieldErrorBg;
+ color: $colorFormFieldErrorFg;
+ border-radius: $basicCr;
+ display: block;
+ padding: 1px 6px;
+ &:before {
+ content: $glyph-icon-alert-triangle;
+ display: inline;
+ font-family: symbolsfont;
+ margin-right: $interiorMarginSm;
+ }
+ }
+}
+
+.tree .inspector-config {
+ margin-left: $treeVCW + $interiorMarginLg;
+}
+
diff --git a/platform/commonUI/general/res/sass/_main.scss b/platform/commonUI/general/res/sass/_main.scss
index 28465cf49f..395b54eb6c 100644
--- a/platform/commonUI/general/res/sass/_main.scss
+++ b/platform/commonUI/general/res/sass/_main.scss
@@ -70,6 +70,7 @@
@import "fixed-position";
@import "lists/tabular";
@import "plots/plots-main";
+@import "plots/legend";
@import "iframe";
@import "views";
@import "items/item";
diff --git a/platform/commonUI/general/res/sass/_views.scss b/platform/commonUI/general/res/sass/_views.scss
index 32094db89d..5ba1862eb3 100644
--- a/platform/commonUI/general/res/sass/_views.scss
+++ b/platform/commonUI/general/res/sass/_views.scss
@@ -5,6 +5,7 @@
}
.l-view-section {
+ //@include test(orange, 0.1);
@include absPosDefault(0);
h2 {
color: #fff;
diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss
index f553324ed6..dbc5868868 100644
--- a/platform/commonUI/general/res/sass/controls/_controls.scss
+++ b/platform/commonUI/general/res/sass/controls/_controls.scss
@@ -150,6 +150,26 @@
}
}
+/******************************************************** VIEW CONTROLS */
+// Expand/collapse > and v arrows, used in tree and plot legend
+// Moved this over from a tree-only context 5/18/17
+
+.view-control {
+ @extend .ui-symbol;
+ cursor: pointer;
+ height: 1em; width: 1em;
+ line-height: inherit;
+ &:before {
+ position: absolute;
+ @include trans-prop-nice(transform, 100ms);
+ content: $glyph-icon-arrow-right;
+ @include transform-origin(center);
+ }
+ &.expanded:before {
+ @include transform(rotate(90deg));
+ }
+}
+
/******************************************************** CUSTOM CHECKBOXES */
label.checkbox.custom,
label.radio.custom {
diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss
index c528f26703..6d362ffff7 100644
--- a/platform/commonUI/general/res/sass/controls/_messages.scss
+++ b/platform/commonUI/general/res/sass/controls/_messages.scss
@@ -398,10 +398,6 @@ body.desktop .t-message-list {
.object-header {
.t-object-alert {
display: inline;
- &.t-alert-unsynced {
- @extend .icon-alert-triangle;
- color: $colorPausedBg;
- }
}
}
}
diff --git a/platform/commonUI/general/res/sass/controls/_palette.scss b/platform/commonUI/general/res/sass/controls/_palette.scss
index dd3ce6d2e9..8b8542b821 100644
--- a/platform/commonUI/general/res/sass/controls/_palette.scss
+++ b/platform/commonUI/general/res/sass/controls/_palette.scss
@@ -20,53 +20,70 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.l-palette {
- $d: 16px;
- $colorsPerRow: 10;
- $m: 1;
-
box-sizing: border-box;
padding: $interiorMargin !important;
+}
- .l-palette-row {
- @include clearfix;
- line-height: $d;
- width: ($d * $colorsPerRow) + ($m * $colorsPerRow);
+.l-palette-row {
+ $d: 16px;
+ $m: 1;
+ $colorsPerRow: 10;
+ display: flex;
+ flex-wrap: wrap;
+ line-height: $d;
+ width: ($d * $colorsPerRow) + ($m * $colorsPerRow);
- &.l-option-row {
- margin-bottom: $interiorMargin;
- .s-palette-item {
- border-color: $colorPaletteFg;
+ &.l-option-row {
+ margin-bottom: $interiorMargin;
+ .s-palette-item {
+ border-color: $colorPaletteFg;
+ }
+ }
+
+ .l-palette-item {
+ box-sizing: border-box;
+ display: block;
+ height: $d; width: $d;
+ min-width: $d;
+ line-height: $d * 0.9;
+ margin: 0 ($m * 1px) ($m * 1px) 0;
+ position: relative;
+ text-align: center;
+ }
+}
+
+.s-palette-item {
+ border: 1px solid transparent;
+ color: $colorPaletteFg;
+ text-shadow: $shdwPaletteFg;
+ @include trans-prop-nice-fade(0.25s);
+ &:hover {
+ @include trans-prop-nice-fade(0);
+ border-color: $colorPaletteSelected !important;
+ }
+ &.selected {
+ border-color: $colorPaletteSelected;
+ box-shadow: $shdwPaletteSelected; //Needed to see selection rect on light colored swatches
+ }
+}
+
+.l-palette-item-label {
+ margin-left: $interiorMargin;
+}
+
+.l-inline-palette {
+ .l-palette-row {
+ width: 100%;
+ .l-palette-item {
+ //@include display(flex);
+ @include flex(1 0 auto);
+ margin: 1px;
+ min-width: auto;
+ width: auto;
+ &:before {
+ content: '';
+ padding-top: 75%;
}
}
-
- .l-palette-item {
- box-sizing: border-box;
- display: block;
- float: left;
- height: $d; width: $d;
- line-height: $d * 0.9;
- margin: 0 ($m * 1px) ($m * 1px) 0;
- position: relative;
- text-align: center;
- }
-
- .s-palette-item {
- border: 1px solid transparent;
- color: $colorPaletteFg;
- text-shadow: $shdwPaletteFg;
- @include trans-prop-nice-fade(0.25s);
- &:hover {
- @include trans-prop-nice-fade(0);
- border-color: $colorPaletteSelected !important;
- }
- &.selected {
- border-color: $colorPaletteSelected;
- box-shadow: $shdwPaletteSelected; //Needed to see selection rect on light colored swatches
- }
- }
-
- .l-palette-item-label {
- margin-left: $interiorMargin;
- }
- }
-}
\ No newline at end of file
+ }
+}
diff --git a/platform/commonUI/general/res/sass/mobile/_tree.scss b/platform/commonUI/general/res/sass/mobile/_tree.scss
index 7995f9eb96..891dd0a17e 100644
--- a/platform/commonUI/general/res/sass/mobile/_tree.scss
+++ b/platform/commonUI/general/res/sass/mobile/_tree.scss
@@ -34,18 +34,7 @@ body.touch {
line-height: $mobileTreeItemH !important;
.view-control {
font-size: 1em;
- margin-right: $interiorMargin;
- width: ceil($mobileTreeItemH * 0.75);
- &.has-children {
- &:before {
- content: $glyph-icon-arrow-down;
- left: 50%;
- @include transform(translateX(-50%) rotate(-90deg));
- }
- &.expanded:before {
- @include transform(translateX(-50%) rotate(0deg));
- }
- }
+ width: ceil($mobileTreeItemH * 0.5);
}
.t-object-label {
line-height: inherit;
diff --git a/platform/commonUI/general/res/sass/plots/_legend.scss b/platform/commonUI/general/res/sass/plots/_legend.scss
new file mode 100644
index 0000000000..9522868510
--- /dev/null
+++ b/platform/commonUI/general/res/sass/plots/_legend.scss
@@ -0,0 +1,208 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2017, 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.
+ *****************************************************************************/
+.gl-plot {
+ .gl-plot-legend {
+ min-height: $plotLegendH;
+
+ .view-control {
+ font-size: 1em;
+ margin-right: $interiorMarginSm;
+ }
+
+ table {
+ table-layout: fixed;
+ tr {
+ display: table-row;
+ }
+ th,
+ td {
+ @include ellipsize(); // Note: this won't work if table-layout uses anything other than fixed.
+ display: table-cell;
+ padding: 1px 3px; // Tighter than standard tabular padding
+ }
+ }
+
+ &.hover-on-plot {
+ // User is hovering over the plot to get a value at a point
+ .hover-value-enabled {
+ background-color: $legendHoverValueBg;
+ border-radius: $smallCr;
+ padding: 0 $interiorMarginSm;
+ &:before {
+ opacity: 0.5;
+ }
+ &.cursor-hover,
+ .value-to-display-nearestTimestamp,
+ .value-to-display-nearestValue
+ {
+ @extend .icon-crosshair-12px;
+ &:before {
+ font-size: 9px;
+ }
+ }
+
+ &.value-to-display-min:before {
+ content: 'MIN ';
+ }
+ &.value-to-display-max:before {
+ content: 'MAX ';
+ }
+ }
+ }
+ }
+
+ &.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
+ &.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
+
+ /***************** GENERAL STYLES, ALL STATES */
+ .plot-legend-item {
+ // General styles for legend items, both expanded and collapsed legend states
+ .plot-series-color-swatch {
+ border-radius: $smallCr;
+ border: 1px solid $colorBodyBg;
+ display: inline-block;
+ height: $plotSwatchD;
+ width: $plotSwatchD;
+ }
+ .plot-series-name {
+ display: inline;
+ }
+
+ .plot-series-value {
+ @include ellipsize();
+ }
+ }
+
+ /***************** GENERAL STYLES, COLLAPSED */
+ &.plot-legend-collapsed {
+ // .plot-legend-item is a span of spans.
+ &.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
+ &.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
+ &.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
+ &.plot-legend-left .gl-plot-legend { margin-right: $interiorMargin; }
+
+ .plot-legend-item {
+ display: flex;
+ align-items: center;
+ &:not(:first-child) {
+ margin-left: $interiorMarginLg;
+ }
+ .plot-series-swatch-and-name,
+ .plot-series-value {
+ @include ellipsize();
+ flex: 1 1 auto;
+ }
+
+ .plot-series-swatch-and-name {
+ margin-right: $interiorMarginSm;
+ }
+
+ .plot-series-value {
+ text-align: left;
+ width: 170px;
+ }
+ }
+ }
+
+ /***************** GENERAL STYLES, EXPANDED */
+ &.plot-legend-expanded {
+ .gl-plot-legend {
+ max-height: 70%;
+ }
+
+ .plot-wrapper-expanded-legend {
+ overflow-y: auto;
+ }
+
+ &.plot-legend-top .gl-plot-legend {
+ margin-bottom: $interiorMargin;
+ }
+ &.plot-legend-bottom .gl-plot-legend {
+ margin-top: $interiorMargin;
+ }
+ }
+
+ /***************** TOP OR BOTTOM */
+ &.plot-legend-top,
+ &.plot-legend-bottom {
+ // General styles when legend is on the top or bottom
+ @extend .l-flex-col;
+ &.plot-legend-collapsed {
+ // COLLAPSED ON TOP OR BOTTOM
+ .plot-wrapper-collapsed-legend {
+ display: flex;
+ flex: 1 1 auto;
+ overflow: hidden;
+ }
+ }
+ }
+
+ /***************** EITHER SIDE */
+ &.plot-legend-left,
+ &.plot-legend-right {
+ @extend .l-flex-row;
+ // If the legend is expanded, use flex-col instead so that the legend gets the width it needs.
+ &.plot-legend-expanded {
+ // EXPANDED, ON EITHER SIDE
+ @extend .l-flex-col;
+ }
+
+ &.plot-legend-collapsed {
+ // COLLAPSED, ON EITHER SIDE
+ .gl-plot-legend {
+ max-height: inherit;
+ width: 25%;
+ }
+ .plot-wrapper-collapsed-legend {
+ display: flex;
+ flex-flow: column nowrap;
+ min-width: 0;
+ flex: 1 1 auto;
+ overflow-y: auto;
+ }
+ .plot-legend-item {
+ margin-bottom: 1px;
+ margin-left: 0;
+ flex-wrap: wrap;
+ .plot-series-swatch-and-name {
+ flex: 0 1 auto;
+ min-width: 20%;
+ }
+ .plot-series-value {
+ flex: 0 1 auto;
+ width: auto;
+ }
+ }
+ }
+ }
+
+ /***************** ON BOTTOM OR RIGHT */
+ &.plot-legend-right:not(.plot-legend-expanded),
+ &.plot-legend-bottom {
+ .gl-plot-legend {
+ order: 2;
+ }
+ .plot-wrapper-axis-and-display-area {
+ order: 1;
+ }
+ }
+}
diff --git a/platform/commonUI/general/res/sass/plots/_plots-main.scss b/platform/commonUI/general/res/sass/plots/_plots-main.scss
index deac69ad88..887decba17 100644
--- a/platform/commonUI/general/res/sass/plots/_plots-main.scss
+++ b/platform/commonUI/general/res/sass/plots/_plots-main.scss
@@ -20,18 +20,64 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.abs.holder-plot {
- // Fend off the scrollbar when less than min-height;
- right: $interiorMargin;
+ right: $interiorMargin; // Fend off the scrollbar when less than min-height;
+ .t-object-alert.t-alert-unsynced {
+ display: none;
+ }
}
+/********************************************* STACKED PLOT LAYOUT */
+.t-plot-stacked {
+ .l-view-section {
+ // Make this a flex container
+ display: flex;
+ flex-flow: column nowrap;
+ .gl-plot.child-frame {
+ mct-plot {
+ display: flex;
+ flex: 1 1 auto;
+ height: 100%;
+ position: relative;
+ }
+ flex: 1 1 auto;
+ &:not(:first-child) {
+ margin-top: $interiorMargin;
+ }
+ }
+ }
+
+ .s-status-timeconductor-unsynced .holder-plot {
+ .t-object-alert.t-alert-unsynced {
+ display: block;
+ }
+ }
+
+}
+
+
+
.gl-plot {
color: $colorPlotFg;
+ display: flex;
font-size: 0.7rem;
position: relative;
width: 100%;
height: 100%;
min-height: $plotMinH;
+ /********************************************* AXIS AND DISPLAY AREA */
+ .plot-wrapper-axis-and-display-area {
+ margin-top: $interiorMargin; // Keep the top tick label from getting clipped
+ position: relative;
+ flex: 1 1 auto;
+ .t-object-alert {
+ position: absolute;
+ display: block;
+ font-size: 1.5em;
+ top: $interiorMarginSm; left: $interiorMarginSm;
+ }
+ }
+
.gl-plot-wrapper-display-area-and-x-axis {
// Holds the plot area and the X-axis only
position: absolute;
@@ -49,7 +95,6 @@
}
.gl-plot-axis-area.gl-plot-x {
- //@include test(green);
top: auto;
right: 0;
bottom: 0;
@@ -63,7 +108,7 @@
.gl-plot-axis-area {
position: absolute;
&.gl-plot-y {
- top: $plotLegendH + $interiorMargin;
+ top: nth($plotDisplayArea, 1);
right: auto;
bottom: nth($plotDisplayArea, 3);
left: 0;
@@ -158,17 +203,6 @@
}
}
- .gl-plot-legend {
- position: absolute;
- top: 0;
- right: 0;
- bottom: auto;
- left: 0;
- height: $plotLegendH;
- overflow-x: hidden;
- overflow-y: auto;
- }
-
/****************************** Limits and Out-of-Bounds data */
.l-limit-bar,
@@ -235,39 +269,6 @@
border: 1px solid $colorPlotAreaBorder;
}
-.gl-plot-legend,
-.legend {
- .plot-legend-item,
- .legend-item {
- display: inline-block;
- margin-right: $interiorMarginLg;
- margin-bottom: $interiorMarginSm;
- span {
- vertical-align: middle;
- }
- .plot-color-swatch,
- .color-swatch {
- border-radius: 2px;
- display: inline-block;
- height: $plotSwatchD;
- width: $plotSwatchD;
- }
- }
-}
-
-.gl-plot-legend {
- .plot-legend-item {
- border-radius: $smallCr;
- line-height: 1.5em;
- padding: 0px $itemPadLR;
- .plot-color-swatch {
- border: 1px solid $colorBodyBg;
- height: $plotSwatchD + 1;
- width: $plotSwatchD + 1;
- }
- }
-}
-
.tick {
position: absolute;
border: 0 $colorPlotHash solid;
diff --git a/platform/commonUI/general/res/sass/tree/_tree.scss b/platform/commonUI/general/res/sass/tree/_tree.scss
index 24e4bd0904..ba8be980a9 100644
--- a/platform/commonUI/general/res/sass/tree/_tree.scss
+++ b/platform/commonUI/general/res/sass/tree/_tree.scss
@@ -23,7 +23,7 @@
ul.tree {
@include menuUlReset();
@include user-select(none);
- li {
+ > li {
display: block;
position: relative;
}
@@ -53,12 +53,10 @@ ul.tree {
.view-control {
color: $colorItemTreeVC;
margin-right: $interiorMargin;
- height: 100%;
- line-height: inherit;
width: $treeVCW;
- &:before { display: none; }
- &.has-children {
- &:before { display: block; }
+ &:before { display: block; }
+ &.no-children {
+ &:before { display: none; }
}
}
diff --git a/platform/commonUI/general/src/ui/TreeNodeView.js b/platform/commonUI/general/src/ui/TreeNodeView.js
index 7c43d0d5f0..1faacc711f 100644
--- a/platform/commonUI/general/src/ui/TreeNodeView.js
+++ b/platform/commonUI/general/src/ui/TreeNodeView.js
@@ -83,9 +83,9 @@ define([
this.activeObject = domainObject;
if (domainObject && domainObject.hasCapability('composition')) {
- $(this.toggleView.elements()).addClass('has-children');
+ $(this.toggleView.elements()).removeClass('no-children');
} else {
- $(this.toggleView.elements()).removeClass('has-children');
+ $(this.toggleView.elements()).addClass('no-children');
}
if (domainObject && domainObject.hasCapability('status')) {
diff --git a/platform/commonUI/themes/espresso/res/sass/_constants.scss b/platform/commonUI/themes/espresso/res/sass/_constants.scss
index c1186c117f..0bc2948982 100644
--- a/platform/commonUI/themes/espresso/res/sass/_constants.scss
+++ b/platform/commonUI/themes/espresso/res/sass/_constants.scss
@@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
$stylePlotHash: dashed;
$colorPlotAreaBorder: $colorInteriorBorder;
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
+$legendCollapsedNameMaxW: 50%;
+$legendHoverValueBg: rgba($colorBodyFg, 0.1);
// Tree
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
diff --git a/platform/commonUI/themes/snow/res/sass/_constants.scss b/platform/commonUI/themes/snow/res/sass/_constants.scss
index 674886c829..69141eef32 100644
--- a/platform/commonUI/themes/snow/res/sass/_constants.scss
+++ b/platform/commonUI/themes/snow/res/sass/_constants.scss
@@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
$stylePlotHash: dashed;
$colorPlotAreaBorder: $colorInteriorBorder;
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
+$legendCollapsedNameMaxW: 50%;
+$legendHoverValueBg: rgba($colorBodyFg, 0.2);
// Tree
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
diff --git a/platform/features/layout/bundle.js b/platform/features/layout/bundle.js
index d7bb565d08..543aee19d5 100644
--- a/platform/features/layout/bundle.js
+++ b/platform/features/layout/bundle.js
@@ -337,46 +337,6 @@ define([
"conversion": "number[]"
}
]
- },
- {
- "key": "telemetry.panel",
- "name": "Telemetry Panel",
- "cssClass": "icon-telemetry-panel",
- "description": "A panel for collecting telemetry elements.",
- "priority": 899,
- "delegates": [
- "telemetry"
- ],
- "features": "creation",
- "contains": [
- {
- "has": "telemetry"
- }
- ],
- "model": {
- "composition": []
- },
- "properties": [
- {
- "name": "Layout Grid",
- "control": "composite",
- "items": [
- {
- "name": "Horizontal grid (px)",
- "control": "textfield",
- "cssClass": "l-input-sm l-numeric"
- },
- {
- "name": "Vertical grid (px)",
- "control": "textfield",
- "cssClass": "l-input-sm l-numeric"
- }
- ],
- "pattern": "^(\\d*[1-9]\\d*)?$",
- "property": "layoutGrid",
- "conversion": "number[]"
- }
- ]
}
]
}
diff --git a/platform/features/plot/README.md b/platform/features/plot/README.md
deleted file mode 100644
index a4a6537fe1..0000000000
--- a/platform/features/plot/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Plot README
-
-## Chart
-
-The `mct-chart` directive is used to support drawing of simple charts. It is
-present to support the Plot view, and its functionality is limited to the
-functionality that is relevant for that view.
-
-This directive is used at the element level and takes one attribute, `draw`
-which is an Angular expression which will should evaluate to a drawing object.
-This drawing object should contain the following properties:
-
-* `dimensions`: The size, in logical coordinates, of the chart area. A
-two-element array or numbers.
-* `origin`: The position, in logical coordinates, of the lower-left corner of
-the chart area. A two-element array or numbers.
-* `lines`: An array of lines (e.g. as a plot line) to draw, where each line is
-expressed as an object containing:
- * `buffer`: A Float32Array containing points in the line, in logical
- coordinates, in sequential x,y pairs.
- * `color`: The color of the line, as a four-element RGBA array, where
- each element is a number in the range of 0.0-1.0.
- * `points`: The number of points in the line.
-* `boxes`: An array of rectangles to draw in the chart area. Each is an object
-containing:
- * `start`: The first corner of the rectangle, as a two-element array of
- numbers, in logical coordinates.
- * `end`: The opposite corner of the rectangle, as a two-element array of
- numbers, in logical coordinates. color : The color of the line, as a
- four-element RGBA array, where each element is a number in the range of
- 0.0-1.0.
-
-While `mct-chart` is intended to support plots specifically, it does perform
-some useful management of canvas objects (e.g. choosing between WebGL and Canvas
-2D APIs for drawing based on browser support) so its usage is recommended when
-its supported drawing primitives are sufficient for other charting tasks.
-
diff --git a/platform/features/plot/bundle.js b/platform/features/plot/bundle.js
deleted file mode 100644
index 7309197c0a..0000000000
--- a/platform/features/plot/bundle.js
+++ /dev/null
@@ -1,157 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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([
- "./src/MCTChart",
- "./src/PlotController",
- "./src/policies/PlotViewPolicy",
- "./src/PlotOptionsController",
- "./src/services/ExportImageService",
- "text!./res/templates/plot.html",
- "text!./res/templates/plot-options-browse.html",
- 'legacyRegistry'
-], function (
- MCTChart,
- PlotController,
- PlotViewPolicy,
- PlotOptionsController,
- exportImageService,
- plotTemplate,
- plotOptionsBrowseTemplate,
- legacyRegistry
-) {
-
- legacyRegistry.register("platform/features/plot", {
- "name": "Plot view for telemetry",
- "extensions": {
- "views": [
- {
- "name": "Plot",
- "key": "plot",
- "cssClass": "icon-sine",
- "template": plotTemplate,
- "needs": [
- "telemetry"
- ],
- "priority": "preferred",
- "delegation": true
- }
- ],
- "directives": [
- {
- "key": "mctChart",
- "implementation": MCTChart,
- "depends": [
- "$interval",
- "$log"
- ]
- }
- ],
- "controllers": [
- {
- "key": "PlotController",
- "implementation": PlotController,
- "depends": [
- "$scope",
- "$element",
- "exportImageService",
- "telemetryFormatter",
- "telemetryHandler",
- "throttle",
- "PLOT_FIXED_DURATION",
- "openmct"
- ]
- },
- {
- "key": "PlotOptionsController",
- "implementation": PlotOptionsController,
- "depends": [
- "$scope"
- ]
- }
- ],
- "services": [
- {
- "key": "exportImageService",
- "implementation": exportImageService,
- "depends": [
- "$q",
- "$timeout",
- "$log",
- "EXPORT_IMAGE_TIMEOUT"
- ]
-
- }
- ],
- "constants": [
- {
- "key": "PLOT_FIXED_DURATION",
- "value": 900000,
- "priority": "fallback",
- "comment": "Fifteen minutes."
- },
- {
- "key": "EXPORT_IMAGE_TIMEOUT",
- "value": 500,
- "priority": "fallback"
- }
- ],
- "policies": [
- {
- "category": "view",
- "implementation": PlotViewPolicy,
- "depends": [
- "openmct"
- ]
- }
- ],
- "representations": [
- {
- "key": "plot-options-browse",
- "template": plotOptionsBrowseTemplate
- }
- ],
- "licenses": [
- {
- "name": "FileSaver.js",
- "version": "0.0.2",
- "author": "Eli Grey",
- "description": "File download initiator (for file exports)",
- "website": "https://github.com/eligrey/FileSaver.js/",
- "copyright": "Copyright © 2015 Eli Grey.",
- "license": "license-mit",
- "link": "https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md"
- },
- {
- "name": "html2canvas",
- "version": "0.4.1",
- "author": "Niklas von Hertzen",
- "description": "JavaScript HTML renderer",
- "website": "https://github.com/niklasvh/html2canvas",
- "copyright": "Copyright © 2012 Niklas von Hertzen.",
- "license": "license-mit",
- "link": "https://github.com/niklasvh/html2canvas/blob/master/LICENSE"
- }
- ]
- }
- });
-});
diff --git a/platform/features/plot/res/templates/plot-options-browse.html b/platform/features/plot/res/templates/plot-options-browse.html
deleted file mode 100644
index 9dd407cb2d..0000000000
--- a/platform/features/plot/res/templates/plot-options-browse.html
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
diff --git a/platform/features/plot/res/templates/plot.html b/platform/features/plot/res/templates/plot.html
deleted file mode 100644
index 0001afe8b4..0000000000
--- a/platform/features/plot/res/templates/plot.html
+++ /dev/null
@@ -1,165 +0,0 @@
-
-
-
-
-
-
-
-
-
- {{telemetryObject.getModel().name}}
-
-
-
-
-
- {{axes[1].active.name}}
-
-
- {{tick.label | reverse}}
-
-
-
-
-
-
-
-
- {{subplot.getHoverCoordinates()}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{tick.label | reverse}}
-
-
- {{axes[0].active.name}}
-
-
-
-
-
-
-
-
diff --git a/platform/features/plot/src/Canvas2DChart.js b/platform/features/plot/src/Canvas2DChart.js
deleted file mode 100644
index d31155f0b1..0000000000
--- a/platform/features/plot/src/Canvas2DChart.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- /**
- * Create a new chart which uses Canvas's 2D API for rendering.
- *
- * @memberof platform/features/plot
- * @constructor
- * @implements {platform/features/plot.Chart}
- * @param {CanvasElement} canvas the canvas object to render upon
- * @throws {Error} an error is thrown if Canvas's 2D API is unavailable.
- */
- function Canvas2DChart(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.");
- }
- }
-
- // Convert from logical to physical x coordinates
- Canvas2DChart.prototype.x = function (v) {
- return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
- };
-
- // Convert from logical to physical y coordinates
- Canvas2DChart.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
- Canvas2DChart.prototype.setColor = function (color) {
- var 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 + ")";
- };
-
-
- Canvas2DChart.prototype.clear = function () {
- var canvas = this.canvas;
- this.width = canvas.width;
- this.height = canvas.height;
- this.c2d.clearRect(0, 0, this.width, this.height);
- };
-
- Canvas2DChart.prototype.setDimensions = function (newDimensions, newOrigin) {
- this.dimensions = newDimensions;
- this.origin = newOrigin;
- };
-
- Canvas2DChart.prototype.drawLine = function (buf, color, points) {
- var i;
-
- this.setColor(color);
-
- // Configure context to draw two-pixel-thick lines
- this.c2d.lineWidth = 2;
-
- // 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();
- };
-
- Canvas2DChart.prototype.drawSquare = function (min, max, color) {
- var x1 = this.x(min[0]),
- y1 = this.y(min[1]),
- w = this.x(max[0]) - x1,
- h = this.y(max[1]) - y1;
-
- this.setColor(color);
- this.c2d.fillRect(x1, y1, w, h);
- };
-
- return Canvas2DChart;
- }
-);
diff --git a/platform/features/plot/src/GLChart.js b/platform/features/plot/src/GLChart.js
deleted file mode 100644
index c7fd83752f..0000000000
--- a/platform/features/plot/src/GLChart.js
+++ /dev/null
@@ -1,160 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 GLPlot. Created by vwoeltje on 11/12/14.
- */
-define(
- [],
- function () {
-
- // WebGL shader sources (for drawing plain colors)
- var FRAGMENT_SHADER = [
- "precision mediump float;",
- "uniform vec4 uColor;",
- "void main(void) {",
- "gl_FragColor = uColor;",
- "}"
- ].join('\n'),
- VERTEX_SHADER = [
- "attribute vec2 aVertexPosition;",
- "uniform vec2 uDimensions;",
- "uniform vec2 uOrigin;",
- "void main(void) {",
- "gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);",
- "}"
- ].join('\n');
-
- /**
- * Create a new chart which uses WebGL for rendering.
- *
- * @memberof platform/features/plot
- * @constructor
- * @implements {platform/features/plot.Chart}
- * @param {CanvasElement} canvas the canvas object to render upon
- * @throws {Error} an error is thrown if WebGL is unavailable.
- */
- function GLChart(canvas) {
- var gl = canvas.getContext("webgl", { preserveDrawingBuffer: true }) ||
- canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true }),
- vertexShader,
- fragmentShader,
- program,
- aVertexPosition,
- uColor,
- uDimensions,
- uOrigin;
-
- // Ensure a context was actually available before proceeding
- if (!gl) {
- throw new Error("WebGL unavailable.");
- }
-
- // Initialize shaders
- vertexShader = gl.createShader(gl.VERTEX_SHADER);
- gl.shaderSource(vertexShader, VERTEX_SHADER);
- gl.compileShader(vertexShader);
- fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
- gl.shaderSource(fragmentShader, FRAGMENT_SHADER);
- gl.compileShader(fragmentShader);
-
- // Assemble vertex/fragment shaders into programs
- program = gl.createProgram();
- gl.attachShader(program, vertexShader);
- gl.attachShader(program, fragmentShader);
- gl.linkProgram(program);
- gl.useProgram(program);
-
- // Get locations for attribs/uniforms from the
- // shader programs (to pass values into shaders at draw-time)
- aVertexPosition = gl.getAttribLocation(program, "aVertexPosition");
- uColor = gl.getUniformLocation(program, "uColor");
- uDimensions = gl.getUniformLocation(program, "uDimensions");
- uOrigin = gl.getUniformLocation(program, "uOrigin");
- gl.enableVertexAttribArray(aVertexPosition);
-
- // Create a buffer to holds points which will be drawn
- this.buffer = gl.createBuffer();
-
- // Use a line width of 2.0 for legibility
- gl.lineWidth(2.0);
-
- // Enable blending, for smoothness
- gl.enable(gl.BLEND);
- gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
-
- this.gl = gl;
- this.aVertexPosition = aVertexPosition;
- this.uColor = uColor;
- this.uDimensions = uDimensions;
- this.uOrigin = uOrigin;
- }
-
- // Utility function to handle drawing of a buffer;
- // drawType will determine whether this is a box, line, etc.
- GLChart.prototype.doDraw = function (drawType, buf, color, points) {
- var gl = this.gl;
- gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
- gl.bufferData(gl.ARRAY_BUFFER, buf, gl.DYNAMIC_DRAW);
- gl.vertexAttribPointer(this.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
- gl.uniform4fv(this.uColor, color);
- gl.drawArrays(drawType, 0, points);
- };
-
- GLChart.prototype.clear = function () {
- var gl = this.gl;
-
- // 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.
- gl.viewport(
- 0,
- 0,
- gl.drawingBufferWidth,
- gl.drawingBufferHeight
- );
- gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT);
- };
-
-
- GLChart.prototype.setDimensions = function (dimensions, origin) {
- var gl = this.gl;
- if (dimensions && dimensions.length > 0 &&
- origin && origin.length > 0) {
- gl.uniform2fv(this.uDimensions, dimensions);
- gl.uniform2fv(this.uOrigin, origin);
- }
- };
-
- GLChart.prototype.drawLine = function (buf, color, points) {
- this.doDraw(this.gl.LINE_STRIP, buf, color, points);
- };
-
- GLChart.prototype.drawSquare = function (min, max, color) {
- this.doDraw(this.gl.TRIANGLE_FAN, new Float32Array(
- min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])
- ), color, 4);
- };
-
- return GLChart;
- }
-);
diff --git a/platform/features/plot/src/MCTChart.js b/platform/features/plot/src/MCTChart.js
deleted file mode 100644
index 7dbc3d5c3e..0000000000
--- a/platform/features/plot/src/MCTChart.js
+++ /dev/null
@@ -1,250 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["./GLChart", "./Canvas2DChart"],
- function (GLChart, Canvas2DChart) {
-
- var TEMPLATE = "";
-
- /**
- * The mct-chart directive provides a canvas element which can be
- * drawn upon, to support Plot view and similar visualizations.
- *
- * This directive takes one attribute, "draw", which is an Angular
- * expression which will be two-way bound to a drawing object. This
- * drawing object should contain:
- *
- * * `dimensions`: An object describing the logical bounds of the
- * drawable area, containing two fields:
- * * `origin`: The position, in logical coordinates, of the
- * lower-left corner of the chart area. A two-element array.
- * * `dimensions`: A two-element array containing the width
- * and height of the chart area, in logical coordinates.
- * * `lines`: An array of lines to be drawn, where each line is
- * expressed as an object containing:
- * * `buffer`: A Float32Array containing points in the line,
- * in logical coordinate, in sequential x/y pairs.
- * * `color`: The color of the line, as a four-element RGBA
- * array, where each element is in the range of 0.0-1.0
- * * `points`: The number of points in the line.
- * * `boxes`: An array of rectangles to draw in the chart area
- * (used for marquee zoom). Each is an object containing:
- * * `start`: The first corner of the rectangle (as a two-element
- * array, logical coordinates)
- * * `end`: The opposite corner of the rectangle (again, as a
- * two-element array)
- * * `color`: The color of the box, as a four-element RGBA
- * array, where each element is in the range of 0.0-1.0
- *
- * @memberof platform/features/plot
- * @constructor
- */
- function MCTChart($interval, $log) {
- // Get an underlying chart implementation
- function getChart(Charts, canvas) {
- // Try the first available option...
- var Chart = Charts[0];
-
- // This function recursively try-catches all options;
- // if these all fail, issue a warning.
- if (!Chart) {
- $log.warn("Cannot initialize mct-chart.");
- return undefined;
- }
-
- // Try first option; if it fails, try remaining options
- try {
- return new Chart(canvas);
- } catch (e) {
- $log.warn([
- "Could not instantiate chart",
- Chart.name,
- ";",
- e.message
- ].join(" "));
-
- return getChart(Charts.slice(1), canvas);
- }
- }
-
- function linkChart(scope, element) {
- var canvas = element.find("canvas")[0],
- activeInterval,
- chart;
-
- // Handle drawing, based on contents of the "draw" object
- // in scope
- function doDraw(draw) {
- // Ensure canvas context has same resolution
- // as canvas element
- canvas.width = canvas.offsetWidth;
- canvas.height = canvas.offsetHeight;
-
- // Clear previous contents
- chart.clear();
-
- // Nothing to draw if no draw object defined
- if (!draw) {
- return;
- }
-
- // Set logical boundaries for the chart
- chart.setDimensions(
- draw.dimensions || [1, 1],
- draw.origin || [0, 0]
- );
-
- // Draw line segments
- (draw.lines || []).forEach(function (line) {
- chart.drawLine(
- line.buffer,
- line.color,
- line.points
- );
- });
-
- // Draw boxes (e.g. marquee zoom rect)
- (draw.boxes || []).forEach(function (box) {
- chart.drawSquare(
- box.start,
- box.end,
- box.color
- );
- });
-
- }
-
- // Issue a drawing call, if-and-only-if canvas size
- // has changed. This will be called on a timer, since
- // there is no event to depend on.
- function drawIfResized() {
- if (canvas.width !== canvas.offsetWidth ||
- canvas.height !== canvas.offsetHeight) {
- doDraw(scope.draw);
- scope.$apply();
- }
- }
-
- // Stop watching for changes to size (scope destroyed)
- function releaseInterval() {
- if (activeInterval) {
- $interval.cancel(activeInterval);
- }
- }
-
- // Switch from WebGL to plain 2D if context is lost
- function fallbackFromWebGL() {
- element.html(TEMPLATE);
- canvas = element.find("canvas")[0];
- chart = getChart([Canvas2DChart], canvas);
- if (chart) {
- doDraw(scope.draw);
- }
- }
-
- // Try to initialize a chart.
- chart = getChart([GLChart, Canvas2DChart], canvas);
-
- // If that failed, there's nothing more we can do here.
- // (A warning will already have been issued)
- if (!chart) {
- return;
- }
-
- // WebGL is a bit of a special case; it may work, then fail
- // later for various reasons, so we need to listen for this
- // and fall back to plain canvas drawing when it occurs.
- canvas.addEventListener("webglcontextlost", fallbackFromWebGL);
-
- // Check for resize, on a timer
- activeInterval = $interval(drawIfResized, 1000, 0, false);
-
- // Watch "draw" for external changes to the set of
- // things to be drawn.
- scope.$watchCollection("draw", doDraw);
-
- // Stop checking for resize when scope is destroyed
- scope.$on("$destroy", releaseInterval);
- }
-
- return {
- // Apply directive only to elements
- restrict: "E",
-
- // Template to use (a canvas element)
- template: TEMPLATE,
-
- // Link function; set up scope
- link: linkChart,
-
- // Initial, isolate scope for the directive
- scope: { draw: "=" }
- };
- }
-
- /**
- * @interface platform/features/plot.Chart
- * @private
- */
-
- /**
- * Clear the chart.
- * @method platform/features/plot.Chart#clear
- */
- /**
- * 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
- * @memberof platform/features/plot.Chart#setDimensions
- */
- /**
- * 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
- * @memberof platform/features/plot.Chart#drawLine
- */
- /**
- * 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
- * @memberof platform/features/plot.Chart#drawSquare
- */
-
- return MCTChart;
- }
-);
-
diff --git a/platform/features/plot/src/PlotController.js b/platform/features/plot/src/PlotController.js
deleted file mode 100644
index e1d1e35e7f..0000000000
--- a/platform/features/plot/src/PlotController.js
+++ /dev/null
@@ -1,437 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * This bundle adds a "Plot" view for numeric telemetry data.
- * @namespace platform/features/plot
- */
-define(
- [
- "./elements/PlotUpdater",
- "./elements/PlotPalette",
- "./elements/PlotAxis",
- "./elements/PlotLimitTracker",
- "./elements/PlotTelemetryFormatter",
- "./modes/PlotModeOptions",
- "./SubPlotFactory"
- ],
- function (
- PlotUpdater,
- PlotPalette,
- PlotAxis,
- PlotLimitTracker,
- PlotTelemetryFormatter,
- PlotModeOptions,
- SubPlotFactory
- ) {
-
- var AXIS_DEFAULTS = [
- { "name": "Time" },
- { "name": "Value" }
- ];
-
- /**
- * The PlotController is responsible for any computation/logic
- * associated with displaying the plot view. Specifically, these
- * responsibilities include:
- *
- * * Describing axes and labeling.
- * * Handling user interactions.
- * * Deciding what needs to be drawn in the chart area.
- *
- * @memberof platform/features/plot
- * @constructor
- */
- function PlotController(
- $scope,
- $element,
- exportImageService,
- telemetryFormatter,
- telemetryHandler,
- throttle,
- PLOT_FIXED_DURATION,
- openmct
- ) {
- var self = this,
- plotTelemetryFormatter =
- new PlotTelemetryFormatter(telemetryFormatter),
- subPlotFactory =
- new SubPlotFactory(plotTelemetryFormatter),
- cachedObjects = [],
- updater,
- lastBounds,
- lastRange,
- lastDomain,
- handle;
- var timeAPI = openmct.time;
-
- // Populate the scope with axis information (specifically, options
- // available for each axis.)
- function setupAxes(metadatas) {
- $scope.axes.forEach(function (axis) {
- axis.updateMetadata(metadatas);
- });
- }
-
- // Trigger an update of a specific subplot;
- // used in a loop to update all subplots.
- function updateSubplot(subplot) {
- subplot.update();
- }
-
- // Set up available modes (stacked/overlaid), based on the
- // set of telemetry objects in this plot view.
- function setupModes(telemetryObjects) {
- if (cachedObjects !== telemetryObjects) {
- cachedObjects = telemetryObjects;
- self.modeOptions = new PlotModeOptions(
- telemetryObjects || [],
- subPlotFactory
- );
- }
- }
-
- // Change the displayable bounds
- function setBasePanZoom(bounds) {
- var start = bounds.start,
- end = bounds.end;
- if (updater) {
- updater.setDomainBounds(start, end);
- self.update();
- }
- lastBounds = bounds;
- }
-
- // Reinstantiate the plot updater (e.g. because we have a
- // new subscription.) This will clear the plot.
- function recreateUpdater() {
- var domain = $scope.axes[0].active.key,
- range = $scope.axes[1].active.key,
- duration = PLOT_FIXED_DURATION;
-
- updater = new PlotUpdater(handle, domain, range, duration);
- lastDomain = domain;
- lastRange = range;
-
- self.limitTracker = new PlotLimitTracker(handle, range);
-
- // Keep any externally-provided bounds
- if (lastBounds) {
- setBasePanZoom(lastBounds);
- }
- }
-
- function getUpdater() {
- if (!updater) {
- recreateUpdater();
- }
- return updater;
- }
-
- // Handle new telemetry data in this plot
- function updateValues() {
- self.pending = false;
- if (handle) {
- setupModes(handle.getTelemetryObjects());
- setupAxes(handle.getMetadata());
- getUpdater().update();
- self.modeOptions.getModeHandler().plotTelemetry(updater);
- self.limitTracker.update();
- self.update();
- }
- }
-
- // Display new historical data as it becomes available
- function addHistoricalData(domainObject, series) {
- self.pending = false;
- getUpdater().addHistorical(domainObject, series);
- self.modeOptions.getModeHandler().plotTelemetry(updater);
- self.update();
- }
-
- // Issue a new request for historical telemetry
- function requestTelemetry() {
- if (handle) {
- handle.request({}, addHistoricalData);
- }
- }
-
- // Requery for data entirely
- function replot() {
- if (handle) {
- updater = undefined;
- requestTelemetry();
- }
- }
-
- function changeTimeOfInterest(timeOfInterest) {
- if (timeOfInterest !== undefined) {
- var bounds = timeAPI.bounds();
- var range = bounds.end - bounds.start;
- $scope.toiPerc = ((timeOfInterest - bounds.start) / range) * 100;
- $scope.toiPinned = true;
- } else {
- $scope.toiPerc = undefined;
- $scope.toiPinned = false;
- }
- }
-
- // Create a new subscription; telemetrySubscriber gets
- // to do the meaningful work here.
- function subscribe(domainObject) {
- if (handle) {
- handle.unsubscribe();
- }
- handle = domainObject && telemetryHandler.handle(
- domainObject,
- updateValues,
- true // Lossless
- );
- replot();
-
- changeTimeOfInterest(timeAPI.timeOfInterest());
- timeAPI.on("timeOfInterest", changeTimeOfInterest);
- }
-
- // Release the current subscription (called when scope is destroyed)
- function releaseSubscription() {
- if (handle) {
- handle.unsubscribe();
- handle = undefined;
- }
- timeAPI.off("timeOfInterest", changeTimeOfInterest);
- }
-
- function requery() {
- self.pending = true;
- releaseSubscription();
- subscribe($scope.domainObject);
- }
-
- function updateDomainFormat() {
- var domainAxis = $scope.axes[0];
- plotTelemetryFormatter
- .setDomainFormat(domainAxis.active.format);
- }
-
- function domainRequery(newDomain) {
- if (newDomain !== lastDomain) {
- updateDomainFormat();
- requery();
- }
- }
-
- function rangeRequery(newRange) {
- if (newRange !== lastRange) {
- requery();
- }
- }
-
- // Respond to a display bounds change (requery for data)
- function changeDisplayBounds(event, bounds, follow) {
- //'hack' for follow mode
- if (follow === true) {
- setBasePanZoom(bounds);
- } else {
- var domainAxis = $scope.axes[0];
-
- if (bounds.domain) {
- domainAxis.chooseOption(bounds.domain);
- }
- updateDomainFormat();
- setBasePanZoom(bounds);
- requery();
- }
- self.setUnsynchedStatus($scope.domainObject, follow && self.isZoomed());
- changeTimeOfInterest(timeAPI.timeOfInterest());
- }
-
- this.modeOptions = new PlotModeOptions([], subPlotFactory);
- this.updateValues = updateValues;
-
- // Create a throttled update function
- this.scheduleUpdate = throttle(function () {
- self.modeOptions.getModeHandler().getSubPlots()
- .forEach(updateSubplot);
- });
-
- self.pending = true;
- self.$element = $element;
- self.exportImageService = exportImageService;
-
- // Initialize axes; will get repopulated when telemetry
- // metadata becomes available.
- $scope.axes = [
- new PlotAxis("domains", [], AXIS_DEFAULTS[0]),
- new PlotAxis("ranges", [], AXIS_DEFAULTS[1])
- ];
-
- //Are some initialized bounds defined?
- var bounds = timeAPI.bounds();
- if (bounds &&
- bounds.start !== undefined &&
- bounds.end !== undefined) {
- changeDisplayBounds(undefined, timeAPI.bounds(), timeAPI.clock() !== undefined);
- }
-
- // Watch for changes to the selected axis
- $scope.$watch("axes[0].active.key", domainRequery);
- $scope.$watch("axes[1].active.key", rangeRequery);
-
- // Subscribe to telemetry when a domain object becomes available
- $scope.$watch('domainObject', subscribe);
-
- // Respond to external bounds changes
- $scope.$on("telemetry:display:bounds", changeDisplayBounds);
-
- // Unsubscribe when the plot is destroyed
- $scope.$on("$destroy", releaseSubscription);
- }
-
- /**
- * Get the color (as a style-friendly string) to use
- * for plotting the trace at the specified index.
- * @param {number} index the index of the trace
- * @returns {string} the color, in #RRGGBB form
- */
- PlotController.prototype.getColor = function (index) {
- return PlotPalette.getStringColor(index);
- };
-
- /**
- * Check if the plot is zoomed or panned out
- * of its default state (to determine whether back/unzoom
- * controls should be shown)
- * @returns {boolean} true if not in default state
- */
- PlotController.prototype.isZoomed = function () {
- return this.modeOptions.getModeHandler().isZoomed();
- };
-
- /**
- * Undo the most recent pan/zoom change and restore
- * the prior state.
- */
- PlotController.prototype.stepBackPanZoom = function () {
- return this.modeOptions.getModeHandler().stepBackPanZoom();
- };
-
- /**
- * Undo all pan/zoom changes and restore the initial state.
- */
- PlotController.prototype.unzoom = function () {
- return this.modeOptions.getModeHandler().unzoom();
- };
-
- /**
- * Get the mode options (Stacked/Overlaid) that are applicable
- * for this plot.
- */
- PlotController.prototype.getModeOptions = function () {
- return this.modeOptions.getModeOptions();
- };
-
- /**
- * Get the current mode that is applicable to this plot. This
- * will include key, name, and cssClass fields.
- */
- PlotController.prototype.getMode = function () {
- return this.modeOptions.getMode();
- };
-
- /**
- * Set the mode which should be active in this plot.
- * @param mode one of the mode options returned from
- * getModeOptions()
- */
- PlotController.prototype.setMode = function (mode) {
- this.modeOptions.setMode(mode);
- this.updateValues();
- };
-
- /**
- * Get all individual plots contained within this Plot view.
- * (Multiple may be contained when in Stacked mode).
- * @returns {SubPlot[]} all subplots in this Plot view
- */
- PlotController.prototype.getSubPlots = function () {
- return this.modeOptions.getModeHandler().getSubPlots();
- };
-
- /**
- * Get the CSS class to apply to the legend for this domain
- * object; this will reflect limit state.
- * @returns {string} the CSS class
- */
- PlotController.prototype.getLegendClass = function (telemetryObject) {
- return this.limitTracker &&
- this.limitTracker.getLegendClass(telemetryObject);
- };
-
- /**
- * Explicitly update all plots.
- */
- PlotController.prototype.update = function () {
- this.scheduleUpdate();
- };
-
- /**
- * Check if a request is pending (to show the wait spinner)
- */
- PlotController.prototype.isRequestPending = function () {
- // Placeholder; this should reflect request state
- // when requesting historical telemetry
- return this.pending;
- };
-
- PlotController.prototype.setUnsynchedStatus = function (domainObject, status) {
- if (domainObject.hasCapability('status')) {
- domainObject.getCapability('status').set('timeconductor-unsynced', status);
- }
- };
-
- /**
- * Export the plot to PNG
- */
- PlotController.prototype.exportPNG = function () {
- var self = this;
- self.hideExportButtons = true;
- self.exportImageService.exportPNG(self.$element[0], "plot.png", 'white').finally(function () {
- self.hideExportButtons = false;
- });
- };
-
- /**
- * Export the plot to JPG
- */
- PlotController.prototype.exportJPG = function () {
- var self = this;
- self.hideExportButtons = true;
- self.exportImageService.exportJPG(self.$element[0], "plot.jpg", 'white').finally(function () {
- self.hideExportButtons = false;
- });
- };
-
- return PlotController;
- }
-);
-
diff --git a/platform/features/plot/src/PlotOptionsController.js b/platform/features/plot/src/PlotOptionsController.js
deleted file mode 100644
index 38fa8c96e3..0000000000
--- a/platform/features/plot/src/PlotOptionsController.js
+++ /dev/null
@@ -1,195 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ['./PlotOptionsForm'],
- function (PlotOptionsForm) {
-
- /**
- * Notes on implementation of plot options
- *
- * Multiple y-axes will have to be handled with multiple forms as
- * they will need to be stored on distinct model object
- *
- * Likewise plot series options per-child will need to be separate
- * forms.
- */
-
- /**
- * The LayoutController is responsible for supporting the
- * Layout view. It arranges frames according to saved configuration
- * and provides methods for updating these based on mouse
- * movement.
- * @memberof platform/features/plot
- * @constructor
- * @param {Scope} $scope the controller's Angular scope
- */
- function PlotOptionsController($scope) {
-
- var self = this;
- this.$scope = $scope;
- this.domainObject = $scope.domainObject;
- this.configuration = this.domainObject.getModel().configuration || {};
- this.plotOptionsForm = new PlotOptionsForm();
- this.composition = [];
- this.watches = [];
-
- /*
- Listen for changes to the domain object and update the object's
- children.
- */
- this.mutationListener = this.domainObject.getCapability('mutation').listen(function (model) {
- if (self.hasCompositionChanged(self.composition, model.composition)) {
- self.updateChildren();
- }
- });
-
- /*
- Set form structures on scope
- */
- $scope.plotSeriesForm = this.plotOptionsForm.plotSeriesForm;
- $scope.xAxisForm = this.plotOptionsForm.xAxisForm;
- $scope.yAxisForm = this.plotOptionsForm.yAxisForm;
-
- $scope.$on("$destroy", function () {
- //Clean up any listeners on destruction of controller
- self.mutationListener();
- });
-
- this.defaultConfiguration();
- this.updateChildren();
-
- /*
- * Setup a number of watches for changes to form values. On
- * change, update the model configuration via mutation
- */
- $scope.$watchCollection('configuration.plot.yAxis', function (newValue, oldValue) {
- self.updateConfiguration(newValue, oldValue);
- });
- $scope.$watchCollection('configuration.plot.xAxis', function (newValue, oldValue) {
- self.updateConfiguration(newValue, oldValue);
- });
-
- this.watchSeries();
-
- }
-
- /**
- * Unregister all watches for series data (ie. the configuration for
- * child objects)
- * @private
- */
- PlotOptionsController.prototype.clearSeriesWatches = function () {
- this.watches.forEach(function (watch) {
- watch();
- });
- this.watches = [];
- };
-
- /**
- * Attach watches for each object in the plot's composition
- * @private
- */
- PlotOptionsController.prototype.watchSeries = function () {
- var self = this;
-
- this.clearSeriesWatches();
-
- (self.$scope.children || []).forEach(function (child, index) {
- self.watches.push(
- self.$scope.$watchCollection(
- 'configuration.plot.series[' + index + ']',
- function (newValue, oldValue) {
- self.updateConfiguration(newValue, oldValue);
- }
- )
- );
- });
- };
-
- /**
- * Determine whether the changes to the model that triggered a
- * mutation event were purely compositional.
- *
- * @private
- */
- PlotOptionsController.prototype.hasCompositionChanged = function (oldComposition, newComposition) {
- // Framed slightly strangely, but the boolean logic is
- // easier to follow for the unchanged case.
- var isUnchanged = oldComposition === newComposition ||
- (
- oldComposition.length === newComposition.length &&
- oldComposition.every(function (currentValue, index) {
- return newComposition[index] && currentValue === newComposition[index];
- })
- );
- return !isUnchanged;
- };
-
- /**
- * Default the plot options model
- *
- * @private
- */
- PlotOptionsController.prototype.defaultConfiguration = function () {
- this.configuration.plot = this.configuration.plot || {};
- this.configuration.plot.xAxis = this.configuration.plot.xAxis || {};
- this.configuration.plot.yAxis = this.configuration.plot.yAxis || {}; // y-axes will be associative array keyed on axis key
- this.configuration.plot.series = this.configuration.plot.series || []; // series will be associative array keyed on sub-object id
- this.$scope.configuration = this.configuration;
- };
-
- /**
- * When a child is added to, or removed from a plot, update the
- * plot options model
- * @private
- */
- PlotOptionsController.prototype.updateChildren = function () {
- var self = this;
- this.domainObject.useCapability('composition').then(function (children) {
- self.$scope.children = children;
- self.composition = self.domainObject.getModel().composition;
- children.forEach(function (child, index) {
- self.configuration.plot.series[index] =
- self.configuration.plot.series[index] || {'id': child.getId()};
- });
- self.watchSeries();
- });
- };
-
- /**
- * On changes to the form, update the configuration on the domain
- * object
- * @private
- */
- PlotOptionsController.prototype.updateConfiguration = function () {
- var self = this;
- this.domainObject.useCapability('mutation', function (model) {
- model.configuration = model.configuration || {};
- model.configuration.plot = self.configuration.plot;
- });
- };
-
- return PlotOptionsController;
- }
-);
-
diff --git a/platform/features/plot/src/PlotOptionsForm.js b/platform/features/plot/src/PlotOptionsForm.js
deleted file mode 100644
index 224ec8530b..0000000000
--- a/platform/features/plot/src/PlotOptionsForm.js
+++ /dev/null
@@ -1,150 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- /**
- * A class for encapsulating structure and behaviour of the plot
- * options form
- * @memberOf platform/features/plot
- * @param topic
- * @constructor
- */
- function PlotOptionsForm() {
-
- /*
- Defined below are the form structures for the plot options.
- */
- this.xAxisForm = {
- 'name': 'x-axis',
- 'sections': [{
- 'name': 'x-axis',
- 'rows': [
- {
- 'name': 'Domain',
- 'control': 'select',
- 'key': 'key',
- 'options': [
- {'name': 'SCET', 'value': 'scet'},
- {'name': 'SCLK', 'value': 'sclk'},
- {'name': 'LST', 'value': 'lst'}
- ]
- }
- ]
- }]};
-
- this.yAxisForm = {
- 'name': 'y-axis',
- 'sections': [{
- // Will need to be repeated for each y-axis, with a
- // distinct name for each. Ideally the name of the axis
- // itself.
- 'name': 'y-axis',
- 'rows': [
- {
- 'name': 'Range',
- 'control': 'select',
- 'key': 'key',
- 'options': [
- {'name': 'EU', 'value': 'eu'},
- {'name': 'DN', 'value': 'dn'},
- {'name': 'Status', 'value': 'status'}
- ]
- },
- {
- 'name': 'Autoscale',
- 'control': 'checkbox',
- 'key': 'autoscale'
- },
- {
- 'name': 'Min',
- 'control': 'textfield',
- 'key': 'min',
- 'pattern': '[0-9]',
- 'inputsize' : 'sm'
- },
- {
- 'name': 'Max',
- 'control': 'textfield',
- 'key': 'max',
- 'pattern': '[0-9]',
- 'inputsize' : 'sm'
- }
- ]
- }]
- };
- this.plotSeriesForm = {
- 'name': 'Series Options',
- 'sections': [
- {
- rows: [
- {
- 'name': 'Color',
- 'control': 'color',
- 'key': 'color'
- }]
- },
- {
- 'rows': [
- {
- 'name': 'Markers',
- 'control': 'checkbox',
- 'key': 'markers',
- 'layout': 'control-first'
- }
- ]
- },
- {
- 'rows': [
- {
- 'name': 'No Line',
- 'control': 'radio',
- 'key': 'lineType',
- 'value': 'noLine',
- 'layout': 'control-first'
- },
- {
- 'name': 'Step Line',
- 'control': 'radio',
- 'key': 'lineType',
- 'value': 'stepLine',
- 'layout': 'control-first'
- },
- {
- 'name': 'Linear Line',
- 'control': 'radio',
- 'key': 'lineType',
- 'value': 'linearLine',
- 'layout': 'control-first'
- }
- ]
- }
- ]
- };
- }
-
- return PlotOptionsForm;
- }
-);
-
diff --git a/platform/features/plot/src/SubPlot.js b/platform/features/plot/src/SubPlot.js
deleted file mode 100644
index a943f1ce70..0000000000
--- a/platform/features/plot/src/SubPlot.js
+++ /dev/null
@@ -1,415 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- [
- './elements/PlotPosition',
- './elements/PlotTickGenerator'
- ],
- function (PlotPosition, PlotTickGenerator) {
-
- var DOMAIN_TICKS = 5,
- RANGE_TICKS = 7;
-
- /**
- * A SubPlot is an individual plot within a Plot View (which
- * may contain multiple plots, specifically when in Stacked
- * plot mode.)
- * @memberof platform/features/plot
- * @constructor
- * @param {DomainObject[]} telemetryObjects the domain objects
- * which will be plotted in this sub-plot
- * @param {PlotPanZoomStack} panZoomStack the stack of pan-zoom
- * states which is applicable to this sub-plot
- * @param {TelemetryFormatter} telemetryFormatter the telemetry
- * formatting service; used to convert domain/range values
- * from telemetry data sets to a human-readable form.
- */
- function SubPlot(telemetryObjects, panZoomStack, telemetryFormatter) {
- // We are used from a template often, so maintain
- // state in local variables to allow for fast look-up,
- // as is normal for controllers.
- this.telemetryObjects = telemetryObjects;
- this.domainTicks = [];
- this.rangeTicks = [];
- this.formatter = telemetryFormatter;
- this.draw = {};
- this.hovering = false;
- this.panZoomStack = panZoomStack;
-
- // Start with the right initial drawing bounds,
- // tick marks
- this.updateDrawingBounds();
- this.updateTicks();
- }
-
- /**
- * Tests whether this subplot has domain data to show for the current pan/zoom level. Absence of domain data
- * implies that there is no range data displayed either
- * @returns {boolean} true if domain data exists for the current pan/zoom level
- */
- SubPlot.prototype.hasDomainData = function () {
- return this.panZoomStack &&
- this.panZoomStack.getDimensions()[0] > 0;
- };
-
- // Utility function for filtering out empty strings.
- function isNonEmpty(v) {
- return typeof v === 'string' && v !== "";
- }
-
- // Converts from pixel coordinates to domain-range,
- // to interpret mouse gestures.
- SubPlot.prototype.mousePositionToDomainRange = function (mousePosition) {
- return new PlotPosition(
- mousePosition.x,
- mousePosition.y,
- mousePosition.width,
- mousePosition.height,
- this.panZoomStack
- ).getPosition();
- };
-
- // Utility function to get the mouse position (in x,y
- // pixel coordinates in the canvas area) from a mouse
- // event object.
- SubPlot.prototype.toMousePosition = function ($event) {
- var bounds = this.subPlotBounds;
-
- return {
- x: $event.clientX - bounds.left,
- y: $event.clientY - bounds.top,
- width: bounds.width,
- height: bounds.height
- };
- };
-
- // Convert a domain-range position to a displayable
- // position. This will subtract the domain offset, which
- // is used to bias domain values to minimize loss-of-precision
- // associated with conversion to a 32-bit floating point
- // format (which is needed in the chart area itself, by WebGL.)
- SubPlot.prototype.toDisplayable = function (position) {
- return [position[0] - this.domainOffset, position[1]];
- };
-
- // Update the current hover coordinates
- SubPlot.prototype.updateHoverCoordinates = function () {
- var formatter = this.formatter;
-
- // Utility, for map/forEach loops. Index 0 is domain,
- // index 1 is range.
- function formatValue(v, i) {
- return i ?
- formatter.formatRangeValue(v) :
- formatter.formatDomainValue(v);
- }
-
- this.hoverCoordinates = this.mousePosition &&
- this.mousePositionToDomainRange(this.mousePosition)
- .map(formatValue)
- .filter(isNonEmpty)
- .join(", ");
- };
-
- // Update the drawable marquee area to reflect current
- // mouse position (or don't show it at all, if no marquee
- // zoom is in progress)
- SubPlot.prototype.updateMarqueeBox = function () {
- // Express this as a box in the draw object, which
- // is passed to an mct-chart in the template for rendering.
- this.draw.boxes = this.marqueeStart ?
- [{
- start: this.toDisplayable(
- this.mousePositionToDomainRange(this.marqueeStart)
- ),
- end: this.toDisplayable(
- this.mousePositionToDomainRange(this.mousePosition)
- ),
- color: [1, 1, 1, 0.5]
- }] : undefined;
- };
-
- // Update the bounds (origin and dimensions) of the drawing area.
- SubPlot.prototype.updateDrawingBounds = function () {
- var panZoom = this.panZoomStack.getPanZoom();
-
- // Communicate pan-zoom state from stack to the draw object
- // which is passed to mct-chart in the template.
- this.draw.dimensions = panZoom.dimensions;
- this.draw.origin = [
- panZoom.origin[0] - this.domainOffset,
- panZoom.origin[1]
- ];
- };
-
- // Update tick marks in scope.
- SubPlot.prototype.updateTicks = function () {
- var tickGenerator =
- new PlotTickGenerator(this.panZoomStack, this.formatter);
-
- this.domainTicks =
- tickGenerator.generateDomainTicks(DOMAIN_TICKS);
- this.rangeTicks =
- tickGenerator.generateRangeTicks(RANGE_TICKS);
- };
-
- SubPlot.prototype.updatePan = function () {
- var start, current, delta, nextOrigin;
-
- // Clear the previous panning pan-zoom state
- this.panZoomStack.popPanZoom();
-
- // Calculate what the new resulting pan-zoom should be
- start = this.mousePositionToDomainRange(
- this.panStart,
- this.panZoomStack
- );
- current = this.mousePositionToDomainRange(
- this.mousePosition,
- this.panZoomStack
- );
-
- delta = [current[0] - start[0], current[1] - start[1]];
- nextOrigin = [
- this.panStartBounds.origin[0] - delta[0],
- this.panStartBounds.origin[1] - delta[1]
- ];
-
- // ...and push a new one at the current mouse position
- this.panZoomStack
- .pushPanZoom(nextOrigin, this.panStartBounds.dimensions);
- };
-
- /**
- * Get the set of domain objects which are being
- * represented in this sub-plot.
- * @returns {DomainObject[]} the domain objects which
- * will have data plotted in this sub-plot
- */
- SubPlot.prototype.getTelemetryObjects = function () {
- return this.telemetryObjects;
- };
-
- /**
- * Get ticks mark information appropriate for using in the
- * template for this sub-plot's domain axis, as prepared
- * by the PlotTickGenerator.
- * @returns {Array} tick marks for the domain axis
- */
- SubPlot.prototype.getDomainTicks = function () {
- return this.domainTicks;
- };
-
- /**
- * Get ticks mark information appropriate for using in the
- * template for this sub-plot's range axis, as prepared
- * by the PlotTickGenerator.
- * @returns {Array} tick marks for the range axis
- */
- SubPlot.prototype.getRangeTicks = function () {
- return this.rangeTicks;
- };
-
- /**
- * Get the drawing object associated with this sub-plot;
- * this object will be passed to the mct-chart in which
- * this sub-plot's lines will be plotted, as its "draw"
- * attribute, and should have the same internal format
- * expected by that directive.
- * @return {object} the drawing object
- */
- SubPlot.prototype.getDrawingObject = function () {
- return this.draw;
- };
-
- /**
- * Get the coordinates (as displayable text) for the
- * current mouse position.
- * @returns {string[]} the displayable domain and range
- * coordinates over which the mouse is hovered
- */
- SubPlot.prototype.getHoverCoordinates = function () {
- return this.hoverCoordinates;
- };
-
- /**
- * Handle mouse movement over the chart area.
- * @param $event the mouse event
- * @memberof platform/features/plot.SubPlot#
- */
- SubPlot.prototype.hover = function ($event) {
- this.hovering = true;
- this.subPlotBounds = $event.target.getBoundingClientRect();
- this.mousePosition = this.toMousePosition($event);
- //If there is a domain to display, show hover coordinates, otherwise hover coordinates are meaningless
- if (this.hasDomainData()) {
- this.updateHoverCoordinates();
- }
- if (this.marqueeStart) {
- this.updateMarqueeBox();
- }
- if (this.panStart) {
- this.updatePan();
- this.updateDrawingBounds();
- this.updateTicks();
- }
- };
-
- /**
- * Continue a previously-start pan or zoom gesture.
- * @param $event the mouse event
- * @memberof platform/features/plot.SubPlot#
- */
- SubPlot.prototype.continueDrag = function ($event) {
- this.mousePosition = this.toMousePosition($event);
- if (this.marqueeStart) {
- this.updateMarqueeBox();
- }
- if (this.panStart) {
- this.updatePan();
- this.updateDrawingBounds();
- this.updateTicks();
- }
- };
-
- /**
- * Initiate a marquee zoom action.
- * @param $event the mouse event
- */
- SubPlot.prototype.startDrag = function ($event) {
- this.subPlotBounds = $event.target.getBoundingClientRect();
- this.mousePosition = this.toMousePosition($event);
- // Treat any modifier key as a pan
- if ($event.altKey || $event.shiftKey || $event.ctrlKey) {
- // Start panning
- this.panStart = this.mousePosition;
- this.panStartBounds = this.panZoomStack.getPanZoom();
- // We're starting a pan, so add this back as a
- // state on the stack; it will get replaced
- // during the pan.
- this.panZoomStack.pushPanZoom(
- this.panStartBounds.origin,
- this.panStartBounds.dimensions
- );
- $event.preventDefault();
- } else {
- // Start marquee zooming
- this.marqueeStart = this.mousePosition;
- this.updateMarqueeBox();
- }
- };
-
- /**
- * Complete a marquee zoom action.
- * @param $event the mouse event
- */
- SubPlot.prototype.endDrag = function ($event) {
- var self = this;
-
- // Perform a marquee zoom.
- function marqueeZoom(start, end) {
- // Determine what boundary is described by the marquee,
- // in domain-range values. Use the minima for origin, so that
- // it doesn't matter what direction the user marqueed in.
- var a = self.mousePositionToDomainRange(start),
- b = self.mousePositionToDomainRange(end),
- origin = [
- Math.min(a[0], b[0]),
- Math.min(a[1], b[1])
- ],
- dimensions = [
- Math.max(a[0], b[0]) - origin[0],
- Math.max(a[1], b[1]) - origin[1]
- ];
-
- // Proceed with zoom if zoom dimensions are non zeros
- if (!(dimensions[0] === 0 && dimensions[1] === 0)) {
- // Push the new state onto the pan-zoom stack
- self.panZoomStack.pushPanZoom(origin, dimensions);
-
- // Make sure tick marks reflect new bounds
- self.updateTicks();
- }
- }
-
- this.mousePosition = this.toMousePosition($event);
- this.subPlotBounds = undefined;
- if (this.marqueeStart) {
- marqueeZoom(this.marqueeStart, this.mousePosition);
- this.marqueeStart = undefined;
- this.updateMarqueeBox();
- this.updateDrawingBounds();
- this.updateTicks();
- }
- if (this.panStart) {
- // End panning
- this.panStart = undefined;
- this.panStartBounds = undefined;
- }
- };
-
- /**
- * Update the drawing bounds, marquee box, and
- * tick marks for this subplot.
- */
- SubPlot.prototype.update = function () {
- this.updateDrawingBounds();
- this.updateMarqueeBox();
- this.updateTicks();
- };
-
- /**
- * Set the domain offset associated with this sub-plot.
- * A domain offset is subtracted from all domain
- * before lines are drawn to avoid artifacts associated
- * with the use of 32-bit floats when domain values
- * are often timestamps (due to insufficient precision.)
- * A SubPlot will be drawing boxes (for marquee zoom) in
- * the same offset coordinate space, so it needs to know
- * the value of this to position that marquee box
- * correctly.
- * @param {number} value the domain offset
- */
- SubPlot.prototype.setDomainOffset = function (value) {
- this.domainOffset = value;
- };
-
- /**
- * When used with no argument, check whether or not the user
- * is currently hovering over this subplot. When used with
- * an argument, set that state.
- * @param {boolean} [state] the new hovering state
- * @returns {boolean} the hovering state
- */
- SubPlot.prototype.isHovering = function (state) {
- if (state !== undefined) {
- this.hovering = state;
- }
- return this.hovering;
- };
-
- return SubPlot;
-
- }
-);
-
diff --git a/platform/features/plot/src/SubPlotFactory.js b/platform/features/plot/src/SubPlotFactory.js
deleted file mode 100644
index d367bd4f39..0000000000
--- a/platform/features/plot/src/SubPlotFactory.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["./SubPlot"],
- function (SubPlot) {
-
- /**
- * Utility factory; wraps the SubPlot constructor and adds
- * in a reference to the telemetryFormatter, which will be
- * used to represent telemetry values (timestamps or data
- * values) as human-readable strings.
- * @memberof platform/features/plot
- * @constructor
- */
- function SubPlotFactory(telemetryFormatter) {
- this.telemetryFormatter = telemetryFormatter;
- }
-
- /**
- * Instantiate a new sub-plot.
- * @param {DomainObject[]} telemetryObjects the domain objects
- * which will be plotted in this sub-plot
- * @param {PlotPanZoomStack} panZoomStack the stack of pan-zoom
- * states which is applicable to this sub-plot
- * @returns {SubPlot} the instantiated sub-plot
- * @method
- */
- SubPlotFactory.prototype.createSubPlot = function (telemetryObjects, panZoomStack) {
- return new SubPlot(
- telemetryObjects,
- panZoomStack,
- this.telemetryFormatter
- );
- };
-
- return SubPlotFactory;
-
- }
-);
diff --git a/platform/features/plot/src/elements/PlotAxis.js b/platform/features/plot/src/elements/PlotAxis.js
deleted file mode 100644
index 6147c4f134..0000000000
--- a/platform/features/plot/src/elements/PlotAxis.js
+++ /dev/null
@@ -1,134 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- /**
- * A PlotAxis provides a template-ready set of options
- * for the domain or range axis, sufficient to populate
- * selectors.
- *
- * @memberof platform/features/plot
- * @constructor
- * @param {string} axisType the field in metadatas to
- * look at for axis options; usually one of
- * "domains" or "ranges"
- * @param {object[]} metadatas metadata objects, as
- * returned by the `getMetadata()` method of
- * a `telemetry` capability.
- * @param {object} defaultValue the value to use for the
- * active state in the event that no options are
- * found; should contain "name" and "key" at
- * minimum.
- *
- */
- function PlotAxis(axisType, metadatas, defaultValue) {
- this.axisType = axisType;
- this.defaultValue = defaultValue;
- this.optionKeys = {};
-
- /**
- * The currently chosen option for this axis. An
- * initial value is provided; this will be updated
- * directly form the plot template.
- * @memberof platform/features/plot.PlotAxis#
- */
- this.active = defaultValue;
-
- /**
- * The set of options applicable for this axis;
- * an array of objects, where each object contains a
- * "key" field and a "name" field (for machine- and
- * human-readable names respectively)
- * @memberof platform/features/plot.PlotAxis#
- */
- this.options = [];
-
- // Initialize options from metadata objects
- this.updateMetadata(metadatas);
- }
-
-
- /**
- * Update axis options to reflect current metadata.
- * @param {TelemetryMetadata[]} metadata objects describing
- * applicable telemetry
- */
- PlotAxis.prototype.updateMetadata = function (metadatas) {
- var axisType = this.axisType,
- optionKeys = this.optionKeys,
- newOptions = {},
- toAdd = [];
-
- function isValid(option) {
- return option && optionKeys[option.key];
- }
-
- metadatas.forEach(function (m) {
- (m[axisType] || []).forEach(function (option) {
- var key = option.key;
- if (!optionKeys[key] && !newOptions[key]) {
- toAdd.push(option);
- }
- newOptions[key] = true;
- });
- });
-
- optionKeys = this.optionKeys = newOptions;
-
- // General approach here is to avoid changing object
- // instances unless something has really changed, since
- // Angular is watching; don't want to trigger extra digests.
- if (!this.options.every(isValid)) {
- this.options = this.options.filter(isValid);
- }
-
- if (toAdd.length > 0) {
- this.options = this.options.concat(toAdd);
- }
-
- if (!isValid(this.active)) {
- this.active = this.options[0] || this.defaultValue;
- }
- };
-
- /**
- * Change the domain/range selection for this axis. If the
- * provided `key` is not recognized as an option, no change
- * will occur.
- * @param {string} key the identifier for the domain/range
- */
- PlotAxis.prototype.chooseOption = function (key) {
- var self = this;
- this.options.forEach(function (option) {
- if (option.key === key) {
- self.active = option;
- }
- });
- };
-
- return PlotAxis;
-
- }
-);
diff --git a/platform/features/plot/src/elements/PlotLimitTracker.js b/platform/features/plot/src/elements/PlotLimitTracker.js
deleted file mode 100644
index f89759b4a2..0000000000
--- a/platform/features/plot/src/elements/PlotLimitTracker.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- /**
- * Tracks the limit state of telemetry objects being plotted.
- * @memberof platform/features/plot
- * @constructor
- * @param {platform/telemetry.TelemetryHandle} handle the handle
- * to telemetry access
- * @param {string} range the key to use when looking up range values
- */
- function PlotLimitTracker(handle, range) {
- this.handle = handle;
- this.range = range;
- this.legendClasses = {};
- }
-
- /**
- * Update limit states to reflect the latest data.
- */
- PlotLimitTracker.prototype.update = function () {
- var legendClasses = {},
- range = this.range,
- handle = this.handle;
-
- function updateLimit(telemetryObject) {
- var limit = telemetryObject.getCapability('limit'),
- datum = handle.getDatum(telemetryObject);
-
- if (limit && datum) {
- legendClasses[telemetryObject.getId()] =
- (limit.evaluate(datum, range) || {}).cssClass;
- }
- }
-
- handle.getTelemetryObjects().forEach(updateLimit);
-
- this.legendClasses = legendClasses;
- };
-
- /**
- * Get the CSS class associated with any limit violations for this
- * telemetry object.
- * @param {DomainObject} domainObject the telemetry object to check
- * @returns {string} the CSS class name, if any
- */
- PlotLimitTracker.prototype.getLegendClass = function (domainObject) {
- var id = domainObject && domainObject.getId();
- return id && this.legendClasses[id];
- };
-
- return PlotLimitTracker;
-
- }
-);
diff --git a/platform/features/plot/src/elements/PlotLine.js b/platform/features/plot/src/elements/PlotLine.js
deleted file mode 100644
index d98ac53333..0000000000
--- a/platform/features/plot/src/elements/PlotLine.js
+++ /dev/null
@@ -1,118 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ['./PlotSeriesWindow'],
- function (PlotSeriesWindow) {
-
-
- /**
- * Represents a single line or trace of a plot.
- * @param {{PlotLineBuffer}} buffer the plot buffer
- * @constructor
- */
- function PlotLine(buffer) {
- this.buffer = buffer;
- }
-
- /**
- * Add a point to this plot line.
- * @param {number} domainValue the domain value
- * @param {number} rangeValue the range value
- */
- PlotLine.prototype.addPoint = function (domainValue, rangeValue) {
- var buffer = this.buffer,
- index;
-
- // Make sure we got real/useful values here...
- if (domainValue !== undefined && rangeValue !== undefined) {
- index = buffer.findInsertionIndex(domainValue);
-
- // Already in the buffer? Skip insertion
- if (index < 0) {
- return;
- }
-
- // Insert the point
- if (!buffer.insertPoint(domainValue, rangeValue, index)) {
- // If insertion failed, trim from the beginning...
- buffer.trim(1);
- // ...and try again.
- buffer.insertPoint(domainValue, rangeValue, index);
- }
- }
- };
-
- /**
- * Add a series of telemetry data to this plot line.
- * @param {TelemetrySeries} series the data series
- * @param {string} [domain] the key indicating which domain
- * to use when looking up data from this series
- * @param {string} [range] the key indicating which range
- * to use when looking up data from this series
- */
- PlotLine.prototype.addSeries = function (series, domain, range) {
- var buffer = this.buffer;
-
- // Insert a time-windowed data series into the buffer
- function insertSeriesWindow(seriesWindow) {
- var count = seriesWindow.getPointCount();
-
- function doInsert() {
- var firstTimestamp = seriesWindow.getDomainValue(0),
- lastTimestamp = seriesWindow.getDomainValue(count - 1),
- startIndex = buffer.findInsertionIndex(firstTimestamp),
- endIndex = buffer.findInsertionIndex(lastTimestamp);
-
- // Does the whole series fit in between two adjacent indexes?
- if ((startIndex === endIndex) && startIndex > -1) {
- // Insert it in between
- buffer.insert(seriesWindow, startIndex);
- } else {
- // Split it up, and add the two halves
- seriesWindow.split().forEach(insertSeriesWindow);
- }
- }
-
- // Only insert if there are points to insert
- if (count > 0) {
- doInsert();
- }
- }
-
- // Should try to add via insertion if a
- // clear insertion point is available;
- // if not, should split and add each half.
- // Insertion operation also needs to factor out
- // redundant timestamps, for overlapping data
- insertSeriesWindow(new PlotSeriesWindow(
- series,
- domain,
- range,
- 0,
- series.getPointCount()
- ));
- };
-
- return PlotLine;
- }
-);
diff --git a/platform/features/plot/src/elements/PlotLineBuffer.js b/platform/features/plot/src/elements/PlotLineBuffer.js
deleted file mode 100644
index b46537e44b..0000000000
--- a/platform/features/plot/src/elements/PlotLineBuffer.js
+++ /dev/null
@@ -1,268 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- /**
- * Contains the buffer used to draw a plot.
- * @param {number} domainOffset number to subtract from domain values
- * @param {number} initialSize initial buffer size
- * @param {number} maxSize maximum buffer size
- * @memberof platform/features/plot
- * @constructor
- */
- function PlotLineBuffer(domainOffset, initialSize, maxSize) {
- this.buffer = new Float32Array(initialSize * 2);
- this.rangeExtrema =
- [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY];
- this.length = 0;
- this.domainOffset = domainOffset;
- this.initialSize = initialSize;
- this.maxSize = maxSize;
- }
-
- // Binary search for an insertion index
- PlotLineBuffer.prototype.binSearch = function (value, min, max) {
- var mid = Math.floor((min + max) / 2),
- found = this.buffer[mid * 2];
-
- // On collisions, insert at same index
- if (found === value) {
- return mid;
- }
-
- // Otherwise, if we're down to a single index,
- // we've found our insertion point
- if (min >= max) {
- // Compare the found timestamp with the search
- // value to decide if we'll insert after or before.
- return min + ((found < value) ? 1 : 0);
- }
-
- // Finally, do the recursive step
- if (found < value) {
- return this.binSearch(value, mid + 1, max);
- } else {
- return this.binSearch(value, min, mid - 1);
- }
- };
-
- // Increase the size of the buffer
- PlotLineBuffer.prototype.doubleBufferSize = function () {
- var sz = Math.min(this.maxSize * 2, this.buffer.length * 2),
- canDouble = sz > this.buffer.length,
- doubled = canDouble && new Float32Array(sz);
-
- if (canDouble) {
- doubled.set(this.buffer); // Copy contents of original
- this.buffer = doubled;
- }
-
- return canDouble;
- };
-
- // Decrease the size of the buffer
- PlotLineBuffer.prototype.halveBufferSize = function () {
- var sz = Math.max(this.initialSize * 2, this.buffer.length / 2),
- canHalve = sz < this.buffer.length;
-
- if (canHalve) {
- this.buffer = new Float32Array(this.buffer.subarray(0, sz));
- }
-
- return canHalve;
- };
-
- // Set a value in the buffer
- PlotLineBuffer.prototype.setValue = function (index, domainValue, rangeValue) {
- this.buffer[index * 2] = domainValue - this.domainOffset;
- this.buffer[index * 2 + 1] = rangeValue;
- // Track min/max of range values (min/max for
- // domain values can be read directly from buffer)
- this.rangeExtrema[0] = Math.min(this.rangeExtrema[0], rangeValue);
- this.rangeExtrema[1] = Math.max(this.rangeExtrema[1], rangeValue);
- };
-
- /**
- * Get the WebGL-displayable buffer of points to plot.
- * @returns {Float32Array} displayable buffer for this line
- */
- PlotLineBuffer.prototype.getBuffer = function () {
- return this.buffer;
- };
-
- /**
- * Get the number of points stored in this buffer.
- * @returns {number} the number of points stored
- */
- PlotLineBuffer.prototype.getLength = function () {
- return this.length;
- };
-
- /**
- * Get the min/max range values that are currently in this
- * buffer. Unlike range extrema, these will change as the
- * buffer gets trimmed.
- * @returns {number[]} min, max domain values
- */
- PlotLineBuffer.prototype.getDomainExtrema = function () {
- // Since these are ordered in the buffer, assume
- // these are the values at the first and last index
- return [
- this.buffer[0] + this.domainOffset,
- this.buffer[this.length * 2 - 2] + this.domainOffset
- ];
- };
-
- /**
- * Get the min/max range values that have been observed for this
- * buffer. Note that these values may have been trimmed out at
- * some point.
- * @returns {number[]} min, max range values
- */
- PlotLineBuffer.prototype.getRangeExtrema = function () {
- return this.rangeExtrema;
- };
-
- /**
- * Remove values from this buffer.
- * Normally, values are removed from the start
- * of the buffer; a truthy value in the second argument
- * will cause values to be removed from the end.
- * @param {number} count number of values to remove
- * @param {boolean} [fromEnd] true if the most recent
- * values should be removed
- */
- PlotLineBuffer.prototype.trim = function (count, fromEnd) {
- // If we're removing values from the start...
- if (!fromEnd) {
- // ...do so by shifting buffer contents over
- this.buffer.set(this.buffer.subarray(2 * count));
- }
- // Reduce used buffer size accordingly
- this.length -= count;
- // Finally, if less than half of the buffer is being
- // used, free up some memory.
- if (this.length < this.buffer.length / 4) {
- this.halveBufferSize();
- }
- };
-
- /**
- * Insert data from the provided series at the specified
- * index. If this would exceed the buffer's maximum capacity,
- * this operation fails and the buffer is unchanged.
- * @param {TelemetrySeries} series the series to insert
- * @param {number} index the index at which to insert this
- * series
- * @returns {boolean} true if insertion succeeded; otherwise
- * false
- */
- PlotLineBuffer.prototype.insert = function (series, index) {
- var sz = series.getPointCount(),
- i;
-
- // Don't allow append after the end; that doesn't make sense
- index = Math.min(index, this.length);
-
- // Resize if necessary
- while (sz > ((this.buffer.length / 2) - this.length)) {
- if (!this.doubleBufferSize()) {
- // Can't make room for this, insertion fails
- return false;
- }
- }
-
- // Shift data over if necessary
- if (index < this.length) {
- this.buffer.set(
- this.buffer.subarray(index * 2, this.length * 2),
- (index + sz) * 2
- );
- }
-
- // Insert data into the set
- for (i = 0; i < sz; i += 1) {
- this.setValue(
- i + index,
- series.getDomainValue(i),
- series.getRangeValue(i)
- );
- }
-
- // Increase the length
- this.length += sz;
-
- // Indicate that insertion was successful
- return true;
- };
-
- /**
- * Append a single data point.
- * @memberof platform/features/plot.PlotLineBuffer#
- */
- PlotLineBuffer.prototype.insertPoint = function (domainValue, rangeValue) {
- // Ensure there is space for this point
- if (this.length >= (this.buffer.length / 2)) {
- if (!this.doubleBufferSize()) {
- return false;
- }
- }
-
- // Put the data in the buffer
- this.setValue(this.length, domainValue, rangeValue);
-
- // Update length
- this.length += 1;
-
- // Indicate that this was successful
- return true;
- };
-
- /**
- * Find an index for inserting data with this
- * timestamp. The second argument indicates whether
- * we are searching for insert-before or insert-after
- * positions.
- * Timestamps are meant to be unique, so if a collision
- * occurs, this will return -1.
- * @param {number} timestamp timestamp to insert
- * @returns {number} the index for insertion (or -1)
- */
- PlotLineBuffer.prototype.findInsertionIndex = function (timestamp) {
- var value = timestamp - this.domainOffset;
-
- // Handle empty buffer case and check for an
- // append opportunity (which is most common case for
- // real-time data so is optimized-for) before falling
- // back to a binary search for the insertion point.
- return (this.length < 1) ? 0 :
- (value > this.buffer[this.length * 2 - 2]) ? this.length :
- this.binSearch(value, 0, this.length - 1);
- };
-
- return PlotLineBuffer;
- }
-);
-
diff --git a/platform/features/plot/src/elements/PlotPalette.js b/platform/features/plot/src/elements/PlotPalette.js
deleted file mode 100644
index 4eaf833f3c..0000000000
--- a/platform/features/plot/src/elements/PlotPalette.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * Plot palette. Defines colors for various plot lines.
- */
-define(
- function () {
-
- // Prepare different forms of the palette, since we wish to
- // describe colors in several ways (as RGB 0-255, as
- // RGB 0.0-1.0, or as stylesheet-appropriate #-prefixed colors).
- var integerPalette = [
- [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]
- ], stringPalette = integerPalette.map(function (arr) {
- // Convert to # notation for use in styles
- return '#' + arr.map(function (c) {
- return (c < 16 ? '0' : '') + c.toString(16);
- }).join('');
- }), floatPalette = integerPalette.map(function (arr) {
- return arr.map(function (c) {
- return c / 255.0;
- }).concat([1]); // RGBA
- });
-
- /**
- * PlotPalette allows a consistent set of colors to be retrieved
- * by index, in various color formats. All PlotPalette methods are
- * static, so there is no need for a constructor call; using
- * this will simply return PlotPalette itself.
- * @memberof platform/features/plot
- * @constructor
- */
- function PlotPalette() {
- return PlotPalette;
- }
-
- /**
- * Look up a color in the plot's palette, by index.
- * This will be returned as a three element array of RGB
- * values, as integers in the range of 0-255.
- * @param {number} i the index of the color to look up
- * @return {number[]} the color, as integer RGB values
- */
- PlotPalette.getIntegerColor = function (i) {
- return integerPalette[Math.floor(i) % integerPalette.length];
- };
-
-
- /**
- * Look up a color in the plot's palette, by index.
- * This will be returned as a three element array of RGB
- * values, in the range of 0.0-1.0.
- *
- * This format is present specifically to support use with
- * WebGL, which expects colors of that form.
- *
- * @param {number} i the index of the color to look up
- * @return {number[]} the color, as floating-point RGB values
- */
- PlotPalette.getFloatColor = function (i) {
- return floatPalette[Math.floor(i) % floatPalette.length];
- };
-
-
- /**
- * Look up a color in the plot's palette, by index.
- * This will be returned as a string using #-prefixed
- * six-digit RGB hex notation (e.g. #FF0000)
- * See http://www.w3.org/TR/css3-color/#rgb-color.
- *
- * This format is useful for representing colors in in-line
- * styles.
- *
- * @param {number} i the index of the color to look up
- * @return {string} the color, as a style-friendly string
- */
- PlotPalette.getStringColor = function (i) {
- return stringPalette[Math.floor(i) % stringPalette.length];
- };
-
- return PlotPalette;
-
- }
-);
diff --git a/platform/features/plot/src/elements/PlotPanZoomStack.js b/platform/features/plot/src/elements/PlotPanZoomStack.js
deleted file mode 100644
index d2c763234f..0000000000
--- a/platform/features/plot/src/elements/PlotPanZoomStack.js
+++ /dev/null
@@ -1,141 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- /**
- * The PlotPanZoomStack is responsible for maintaining the
- * pan-zoom state of a plot (expressed as a boundary starting
- * at an origin and extending to certain dimensions) in a
- * stack, to support the back and unzoom buttons in plot controls.
- *
- * Dimensions and origins are here described each by two-element
- * arrays, where the first element describes a value or quantity
- * along the domain axis, and the second element describes the same
- * along the range axis.
- *
- * @memberof platform/features/plot
- * @constructor
- * @param {number[]} origin the plot's origin, initially
- * @param {number[]} dimensions the plot's dimensions, initially
- */
- function PlotPanZoomStack(origin, dimensions) {
- // Use constructor parameters as the stack's initial state
- this.stack = [{ origin: origin, dimensions: dimensions }];
- }
-
- // Various functions which follow are simply wrappers for
- // normal stack-like array methods, with the exception that
- // they prevent undesired modification and enforce that this
- // stack must remain non-empty.
- // See JSDoc for specific methods below for more detail.
-
- /**
- * Get the current stack depth; that is, the number
- * of items on the stack. A depth of one means that no
- * panning or zooming relative to the base value has
- * been applied.
- * @returns {number} the depth of the stack
- */
- PlotPanZoomStack.prototype.getDepth = function getDepth() {
- return this.stack.length;
- };
-
- /**
- * Push a new pan-zoom state onto the stack; this will
- * become the active pan-zoom state.
- * @param {number[]} origin the new origin
- * @param {number[]} dimensions the new dimensions
- */
- PlotPanZoomStack.prototype.pushPanZoom = function (origin, dimensions) {
- this.stack.push({ origin: origin, dimensions: dimensions });
- };
-
- /**
- * Pop a pan-zoom state from the stack. Whatever pan-zoom
- * state was previously present will become current.
- * If called when there is only one pan-zoom state on the
- * stack, this acts as a no-op (that is, the lowest
- * pan-zoom state on the stack cannot be popped, to ensure
- * that some pan-zoom state is always available.)
- */
- PlotPanZoomStack.prototype.popPanZoom = function popPanZoom() {
- if (this.stack.length > 1) {
- this.stack.pop();
- }
- };
-
- /**
- * Set the base pan-zoom state; that is, the state at the
- * bottom of the stack. This allows the "unzoomed" state of
- * a plot to be updated (e.g. as new data comes in) without
- * interfering with the user's chosen zoom level.
- * @param {number[]} origin the base origin
- * @param {number[]} dimensions the base dimensions
- * @memberof platform/features/plot.PlotPanZoomStack#
- */
- PlotPanZoomStack.prototype.setBasePanZoom = function (origin, dimensions) {
- this.stack[0] = { origin: origin, dimensions: dimensions };
- };
-
- /**
- * Clear the pan-zoom stack down to its bottom element;
- * in effect, pop all elements but the last, e.g. to remove
- * any temporary user modifications to pan-zoom state.
- */
- PlotPanZoomStack.prototype.clearPanZoom = function clearPanZoom() {
- this.stack = [this.stack[0]];
- };
-
- /**
- * Get the current pan-zoom state (the state at the top
- * of the stack), expressed as an object with "origin" and
- * "dimensions" fields.
- * @returns {object} the current pan-zoom state
- */
- PlotPanZoomStack.prototype.getPanZoom = function getPanZoom() {
- return this.stack[this.stack.length - 1];
- };
-
- /**
- * Get the current origin, as represented on the top of the
- * stack.
- * @returns {number[]} the current plot origin
- */
- PlotPanZoomStack.prototype.getOrigin = function getOrigin() {
- return this.getPanZoom().origin;
- };
-
- /**
- * Get the current dimensions, as represented on the top of
- * the stack.
- * @returns {number[]} the current plot dimensions
- */
- PlotPanZoomStack.prototype.getDimensions = function getDimensions() {
- return this.getPanZoom().dimensions;
- };
-
- return PlotPanZoomStack;
- }
-);
diff --git a/platform/features/plot/src/elements/PlotPanZoomStackGroup.js b/platform/features/plot/src/elements/PlotPanZoomStackGroup.js
deleted file mode 100644
index 5083978167..0000000000
--- a/platform/features/plot/src/elements/PlotPanZoomStackGroup.js
+++ /dev/null
@@ -1,167 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ['./PlotPanZoomStack'],
- function (PlotPanZoomStack) {
-
- /**
- * A plot pan zoom stack group provides a collection of individual
- * pan-zoom stacks that synchronize upon the domain axis, but
- * remain independent upon the range axis. This supports panning
- * and zooming in stacked-plot mode (and, importantly,
- * stepping back through those states.)
- * @memberof platform/features/plot
- * @constructor
- * @param {number} count the number of stacks to include in this
- * group
- */
- function PlotPanZoomStackGroup(count) {
- var self = this;
-
- // Push a pan-zoom state; the index argument identifies
- // which stack originated the request (all other stacks
- // will ignore the range part of the change.)
- function pushPanZoom(origin, dimensions, index) {
- self.stacks.forEach(function (stack, i) {
- if (i === index) {
- // Do a normal push for the specified stack
- stack.pushPanZoom(origin, dimensions);
- } else {
- // For other stacks, do a push, but repeat
- // their current range axis bounds.
- stack.pushPanZoom(
- [origin[0], stack.getOrigin()[1]],
- [dimensions[0], stack.getDimensions()[1]]
- );
- }
- });
- }
-
-
- // Decorate a pan-zoom stack; returns an object with
- // the same interface, but whose stack-mutation methods
- // effect all items in the group.
- function decorateStack(stack, index) {
- var result = Object.create(stack);
-
- // Use the methods defined above
- result.pushPanZoom = function (origin, dimensions) {
- pushPanZoom(origin, dimensions, index);
- };
- result.setBasePanZoom = function () {
- self.setBasePanZoom.apply(self, arguments);
- };
- result.popPanZoom = function () {
- self.popPanZoom.apply(self, arguments);
- };
- result.clearPanZoom = function () {
- self.clearPanZoom.apply(self, arguments);
- };
-
- return result;
- }
-
- // Create the stacks in this group ...
- this.stacks = [];
- while (this.stacks.length < count) {
- this.stacks.push(new PlotPanZoomStack([], []));
- }
- // ... and their decorated-to-synchronize versions.
- this.decoratedStacks = this.stacks.map(decorateStack);
- }
-
- /**
- * Pop a pan-zoom state from all stacks in the group.
- * If called when there is only one pan-zoom state on each
- * stack, this acts as a no-op (that is, the lowest
- * pan-zoom state on the stack cannot be popped, to ensure
- * that some pan-zoom state is always available.)
- */
- PlotPanZoomStackGroup.prototype.popPanZoom = function () {
- this.stacks.forEach(function (stack) {
- stack.popPanZoom();
- });
- };
-
- /**
- * Set the base pan-zoom state for all stacks in this group.
- * This changes the state at the bottom of each stack.
- * This allows the "unzoomed" state of plots to be updated
- * (e.g. as new data comes in) without
- * interfering with the user's chosen pan/zoom states.
- * @param {number[]} origin the base origin
- * @param {number[]} dimensions the base dimensions
- */
- PlotPanZoomStackGroup.prototype.setBasePanZoom = function (origin, dimensions) {
- this.stacks.forEach(function (stack) {
- stack.setBasePanZoom(origin, dimensions);
- });
- };
-
- /**
- * Clear all pan-zoom stacks in this group down to
- * their bottom element; in effect, pop all elements
- * but the last, e.g. to remove any temporary user
- * modifications to pan-zoom state.
- */
- PlotPanZoomStackGroup.prototype.clearPanZoom = function () {
- this.stacks.forEach(function (stack) {
- stack.clearPanZoom();
- });
- };
-
- /**
- * Get the current stack depth; that is, the number
- * of items on each stack in the group.
- * A depth of one means that no
- * panning or zooming relative to the base value has
- * been applied.
- * @returns {number} the depth of the stacks in this group
- */
- PlotPanZoomStackGroup.prototype.getDepth = function () {
- // All stacks are kept in sync, so look up depth
- // from the first one.
- return this.stacks.length > 0 ? this.stacks[0].getDepth() : 0;
- };
-
- /**
- * Get a specific pan-zoom stack in this group.
- * Stacks are specified by index; this index must be less
- * than the count provided at construction time, and must
- * not be less than zero.
- * The stack returned by this function will be synchronized
- * to other stacks in this group; that is, mutating that
- * stack directly will result in other stacks in this group
- * undergoing similar updates to ensure that domain bounds
- * remain the same.
- * @param {number} index the index of the stack to get
- * @returns {PlotPanZoomStack} the pan-zoom stack in the
- * group identified by that index
- */
- PlotPanZoomStackGroup.prototype.getPanZoomStack = function (index) {
- return this.decoratedStacks[index];
- };
-
- return PlotPanZoomStackGroup;
- }
-);
diff --git a/platform/features/plot/src/elements/PlotPosition.js b/platform/features/plot/src/elements/PlotPosition.js
deleted file mode 100644
index a649f29946..0000000000
--- a/platform/features/plot/src/elements/PlotPosition.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- /**
- * A PlotPosition converts from pixel coordinates to domain-range
- * coordinates, based on the current plot boundary as described on
- * the pan-zoom stack.
- *
- * These coordinates are not updated after construction; that is,
- * they represent the result of the conversion at the time the
- * PlotPosition was instantiated. Care should be taken when retaining
- * PlotPosition objects across changes to the pan-zoom stack.
- *
- * @memberof platform/features/plot
- * @constructor
- * @param {number} x the horizontal pixel position in the plot area
- * @param {number} y the vertical pixel position in the plot area
- * @param {number} width the width of the plot area
- * @param {number} height the height of the plot area
- * @param {PanZoomStack} panZoomStack the applicable pan-zoom stack,
- * used to determine the plot's domain-range boundaries.
- */
- function PlotPosition(x, y, width, height, panZoomStack) {
- var panZoom = panZoomStack.getPanZoom(),
- origin = panZoom.origin,
- dimensions = panZoom.dimensions;
-
- function convert(v, i) {
- return v * dimensions[i] + origin[i];
- }
-
- if (!dimensions || !origin) {
- // We need both dimensions and origin to compute a position
- this.position = [];
- } else {
- // Convert from pixel to domain-range space.
- // Note that range is reversed from the y-axis in pixel space
- //(positive range points up, positive pixel-y points down)
- this.position =
- [x / width, (height - y) / height].map(convert);
- }
- }
-
- /**
- * Get the domain value corresponding to this pixel position.
- * @returns {number} the domain value
- */
- PlotPosition.prototype.getDomain = function () {
- return this.position[0];
- };
-
- /**
- * Get the range value corresponding to this pixel position.
- * @returns {number} the range value
- */
- PlotPosition.prototype.getRange = function () {
- return this.position[1];
- };
-
- /**
- * Get the domain and values corresponding to this
- * pixel position.
- * @returns {number[]} an array containing the domain and
- * the range value, in that order
- */
- PlotPosition.prototype.getPosition = function () {
- return this.position;
- };
-
- return PlotPosition;
- }
-);
diff --git a/platform/features/plot/src/elements/PlotPreparer.js b/platform/features/plot/src/elements/PlotPreparer.js
deleted file mode 100644
index e6da435229..0000000000
--- a/platform/features/plot/src/elements/PlotPreparer.js
+++ /dev/null
@@ -1,153 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * Prepares data to be rendered in a GL Plot. Handles
- * the conversion from data API to displayable buffers.
- */
-define(
- function () {
-
- function identity(x) {
- return x;
- }
-
- /**
- * The PlotPreparer is responsible for handling data sets and
- * preparing them to be rendered. It creates a WebGL-plottable
- * Float32Array for each trace, and tracks the boundaries of the
- * data sets (since this is convenient to do during the same pass).
- * @memberof platform/features/plot
- * @constructor
- * @param {Telemetry[]} datas telemetry data objects
- * @param {string} domain the key to use when looking up domain values
- * @param {string} range the key to use when looking up range values
- */
- function PlotPreparer(datas, domain, range) {
- var index,
- vertices = [],
- max = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
- min = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
- x,
- y,
- domainOffset = Number.POSITIVE_INFINITY;
-
- // Remove any undefined data sets
- datas = (datas || []).filter(identity);
-
- // Do a first pass to determine the domain offset.
- // This will be use to reduce the magnitude of domain values
- // in the buffer, to minimize loss-of-precision when
- // converting to a 32-bit float.
- datas.forEach(function (data) {
- domainOffset = Math.min(data.getDomainValue(0, domain), domainOffset);
- });
-
- // Assemble buffers, and track bounds of the data present
- datas.forEach(function (data, i) {
- vertices.push([]);
- for (index = 0; index < data.getPointCount(); index += 1) {
- x = data.getDomainValue(index, domain);
- y = data.getRangeValue(index, range);
- vertices[i].push(x - domainOffset);
- vertices[i].push(y);
- min[0] = Math.min(min[0], x);
- min[1] = Math.min(min[1], y);
- max[0] = Math.max(max[0], x);
- max[1] = Math.max(max[1], y);
- }
- });
-
- // If range is empty, add some padding
- if (max[1] === min[1]) {
- max[1] = max[1] + 1.0;
- min[1] = min[1] - 1.0;
- }
-
- // Convert to Float32Array
- this.buffers = vertices.map(function (v) {
- return new Float32Array(v);
- });
-
- this.min = min;
- this.max = max;
- this.domainOffset = domainOffset;
- }
-
- /**
- * Get the dimensions which bound all data in the provided
- * data sets. This is given as a two-element array where the
- * first element is domain, and second is range.
- * @returns {number[]} the dimensions which bound this data set
- */
- PlotPreparer.prototype.getDimensions = function () {
- var max = this.max, min = this.min;
- return [max[0] - min[0], max[1] - min[1]];
- };
-
- /**
- * Get the origin of this data set's boundary.
- * This is given as a two-element array where the
- * first element is domain, and second is range.
- * The domain value here is not adjusted by the domain offset.
- * @returns {number[]} the origin of this data set's boundary
- */
- PlotPreparer.prototype.getOrigin = function () {
- return this.min;
- };
-
- /**
- * Get the domain offset; this offset will have been subtracted
- * from all domain values in all buffers returned by this
- * preparer, in order to minimize loss-of-precision due to
- * conversion to the 32-bit float format needed by WebGL.
- * @returns {number} the domain offset
- */
- PlotPreparer.prototype.getDomainOffset = function () {
- return this.domainOffset;
- };
-
- /**
- * Get all renderable buffers for this data set. This will
- * be returned as an array which can be correlated back to
- * the provided telemetry data objects (from the constructor
- * call) by index.
- *
- * Internally, these are flattened; each buffer contains a
- * sequence of alternating domain and range values.
- *
- * All domain values in all buffers will have been adjusted
- * from their original values by subtraction of the domain
- * offset; this minimizes loss-of-precision resulting from
- * the conversion to 32-bit floats, which may otherwise
- * cause aliasing artifacts (particularly for timestamps)
- *
- * @returns {Float32Array[]} the buffers for these traces
- */
- PlotPreparer.prototype.getBuffers = function () {
- return this.buffers;
- };
-
- return PlotPreparer;
-
- }
-);
diff --git a/platform/features/plot/src/elements/PlotSeriesWindow.js b/platform/features/plot/src/elements/PlotSeriesWindow.js
deleted file mode 100644
index 4907254c9c..0000000000
--- a/platform/features/plot/src/elements/PlotSeriesWindow.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- /**
- * Provides a window on a telemetry data series, to support
- * insertion into a plot line.
- * @constructor
- * @memberof platform/features/plot
- * @implements {TelemetrySeries}
- */
- function PlotSeriesWindow(series, domain, range, start, end) {
- this.series = series;
- this.domain = domain;
- this.range = range;
- this.start = start;
- this.end = end;
- }
-
- PlotSeriesWindow.prototype.getPointCount = function () {
- return this.end - this.start;
- };
-
- PlotSeriesWindow.prototype.getDomainValue = function (index) {
- return this.series.getDomainValue(index + this.start, this.domain);
- };
-
- PlotSeriesWindow.prototype.getRangeValue = function (index) {
- return this.series.getRangeValue(index + this.start, this.range);
- };
-
- /**
- * Split this series into two series of equal (or nearly-equal) size.
- * @returns {PlotSeriesWindow[]} two series
- */
- PlotSeriesWindow.prototype.split = function () {
- var mid = Math.floor((this.end + this.start) / 2);
- return ((this.end - this.start) > 1) ?
- [
- new PlotSeriesWindow(
- this.series,
- this.domain,
- this.range,
- this.start,
- mid
- ),
- new PlotSeriesWindow(
- this.series,
- this.domain,
- this.range,
- mid,
- this.end
- )
- ] : [];
- };
-
- return PlotSeriesWindow;
- }
-);
diff --git a/platform/features/plot/src/elements/PlotTelemetryFormatter.js b/platform/features/plot/src/elements/PlotTelemetryFormatter.js
deleted file mode 100644
index d4baaa7061..0000000000
--- a/platform/features/plot/src/elements/PlotTelemetryFormatter.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- var DIGITS = 3;
-
- /**
- * Wraps a `TelemetryFormatter` to provide formats for domain and
- * range values; provides a single place to track domain/range
- * formats within a plot, allowing other plot elements to simply
- * request that values be formatted.
- * @constructor
- * @memberof platform/features/plot
- * @implements {platform/telemetry.TelemetryFormatter}
- * @param {TelemetryFormatter} telemetryFormatter the formatter
- * to wrap.
- */
- function PlotTelemetryFormatter(telemetryFormatter) {
- this.telemetryFormatter = telemetryFormatter;
- }
-
- /**
- * Specify the format to use for domain values.
- * @param {string} key the format's identifier
- */
- PlotTelemetryFormatter.prototype.setDomainFormat = function (key) {
- this.domainFormat = key;
- };
-
- /**
- * Specify the format to use for range values.
- * @param {string} key the format's identifier
- */
- PlotTelemetryFormatter.prototype.setRangeFormat = function (key) {
- this.rangeFormat = key;
- };
-
- PlotTelemetryFormatter.prototype.formatDomainValue = function (value) {
- return this.telemetryFormatter
- .formatDomainValue(value, this.domainFormat);
- };
-
- PlotTelemetryFormatter.prototype.formatRangeValue = function (value) {
- if (typeof value === 'number') {
- return value.toFixed(DIGITS);
- }
-
- return this.telemetryFormatter
- .formatRangeValue(value, this.rangeFormat);
- };
-
- return PlotTelemetryFormatter;
- }
-);
diff --git a/platform/features/plot/src/elements/PlotTickGenerator.js b/platform/features/plot/src/elements/PlotTickGenerator.js
deleted file mode 100644
index c467ca0c76..0000000000
--- a/platform/features/plot/src/elements/PlotTickGenerator.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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 () {
-
- /**
- * The PlotTickGenerator provides labels for ticks along the
- * domain and range axes of the plot, to support the plot
- * template.
- *
- * @memberof platform/features/plot
- * @constructor
- * @param {PlotPanZoomStack} panZoomStack the pan-zoom stack for
- * this plot, used to determine plot boundaries
- * @param {TelemetryFormatter} formatter used to format (for display)
- * domain and range values.
- */
- function PlotTickGenerator(panZoomStack, formatter) {
- this.panZoomStack = panZoomStack;
- this.formatter = formatter;
- }
-
- // For phantomjs compatibility, for headless testing
- // (Function.prototype.bind unsupported)
- function bind(fn, thisObj) {
- return fn.bind ? fn.bind(thisObj) : function () {
- return fn.apply(thisObj, arguments);
- };
- }
-
- // Generate ticks; interpolate from start up to
- // start + span in count steps, using the provided
- // formatter to represent each value.
- PlotTickGenerator.prototype.generateTicks = function (start, span, count, format) {
- var step = span / (count - 1),
- result = [],
- i;
-
- for (i = 0; i < count; i += 1) {
- result.push({
- //If data to show, display label for each tick line, otherwise show lines but suppress labels.
- label: span > 0 ? format(i * step + start) : ''
- });
- }
-
- return result;
- };
-
- /**
- * Generate tick marks for the domain axis.
- * @param {number} count the number of ticks
- * @returns {string[]} labels for those ticks
- */
- PlotTickGenerator.prototype.generateDomainTicks = function (count) {
- var panZoom = this.panZoomStack.getPanZoom();
- return this.generateTicks(
- panZoom.origin[0],
- panZoom.dimensions[0],
- count,
- bind(this.formatter.formatDomainValue, this.formatter)
- );
- };
-
- /**
- * Generate tick marks for the range axis.
- * @param {number} count the number of ticks
- * @returns {string[]} labels for those ticks
- */
- PlotTickGenerator.prototype.generateRangeTicks = function (count) {
- var panZoom = this.panZoomStack.getPanZoom();
- return this.generateTicks(
- panZoom.origin[1],
- panZoom.dimensions[1],
- count,
- bind(this.formatter.formatRangeValue, this.formatter)
- );
- };
-
- return PlotTickGenerator;
- }
-);
diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js
deleted file mode 100644
index 633b9ea9d5..0000000000
--- a/platform/features/plot/src/elements/PlotUpdater.js
+++ /dev/null
@@ -1,353 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ['./PlotLine', './PlotLineBuffer'],
- function (PlotLine, PlotLineBuffer) {
-
- var MAX_POINTS = 86400,
- PADDING_RATIO = 0.10, // Padding percentage for top & bottom
- INITIAL_SIZE = 675; // 1/128 of MAX_POINTS
-
- /**
- * The PlotPreparer is responsible for handling data sets and
- * preparing them to be rendered. It creates a WebGL-plottable
- * Float32Array for each trace, and tracks the boundaries of the
- * data sets (since this is convenient to do during the same pass).
- * @memberof platform/features/plot
- * @constructor
- * @param {TelemetryHandle} handle the handle to telemetry access
- * @param {string} domain the key to use when looking up domain values
- * @param {string} range the key to use when looking up range values
- * @param {number} fixedDuration maximum plot duration to display
- * @param {number} maxPoints maximum number of points to display
- */
- function PlotUpdater(handle, domain, range, fixedDuration, maxPoints) {
- this.handle = handle;
- this.domain = domain;
- this.range = range;
- this.fixedDuration = fixedDuration;
- this.maxPoints = maxPoints;
-
- this.ids = [];
- this.lines = {};
- this.buffers = {};
- this.bufferArray = [];
-
- // Use a default MAX_POINTS if none is provided
- this.maxPoints = maxPoints !== undefined ? maxPoints : MAX_POINTS;
- this.dimensions = [0, 0];
- this.origin = [0, 0];
-
- // Initially prepare state for these objects.
- // Note that this may be an empty array at this time,
- // so we also need to check during update cycles.
- this.update();
- }
-
- // Look up a domain object's id (for mapping, below)
- function getId(domainObject) {
- return domainObject.getId();
- }
-
- // Used in the reduce step of updateExtrema
- function reduceExtrema(a, b) {
- return [Math.min(a[0], b[0]), Math.max(a[1], b[1])];
- }
-
- // Convert a domain/range extrema to plot dimensions
- function dimensionsOf(extrema) {
- return extrema[1] - extrema[0];
- }
-
- // Convert a domain/range extrema to a plot origin
- function originOf(extrema) {
- return extrema[0];
- }
-
- // Check if this set of ids matches the current set of ids
- // (used to detect if line preparation can be skipped)
- PlotUpdater.prototype.idsMatch = function (nextIds) {
- var ids = this.ids;
- return ids.length === nextIds.length &&
- nextIds.every(function (id, index) {
- return ids[index] === id;
- });
- };
-
- // Prepare plot lines for this group of telemetry objects
- PlotUpdater.prototype.prepareLines = function (telemetryObjects) {
- var nextIds = telemetryObjects.map(getId),
- next = {},
- self = this;
-
- // Detect if we already have everything we need prepared
- if (this.idsMatch(nextIds)) {
- // Nothing to prepare, move on
- return;
- }
-
- // Built up a set of ids. Note that we can only
- // create plot lines after our domain offset has
- // been determined.
- if (this.domainOffset !== undefined) {
- // Update list of ids in use
- this.ids = nextIds;
-
- // Create buffers for these objects
- this.bufferArray = this.ids.map(function (id) {
- self.buffers[id] = self.buffers[id] || new PlotLineBuffer(
- self.domainOffset,
- INITIAL_SIZE,
- self.maxPoints
- );
- next[id] =
- self.lines[id] || new PlotLine(self.buffers[id]);
- return self.buffers[id];
- });
- }
-
- // If there are no more lines, clear the domain offset
- if (Object.keys(next).length < 1) {
- this.domainOffset = undefined;
- }
-
- // Update to the current set of lines
- this.lines = next;
- };
-
- // Initialize the domain offset, based on these observed values
- PlotUpdater.prototype.initializeDomainOffset = function (values) {
- this.domainOffset =
- ((this.domainOffset === undefined) && (values.length > 0)) ?
- (values.reduce(function (a, b) {
- return (a || 0) + (b || 0);
- }, 0) / values.length) :
- this.domainOffset;
- };
-
- // Expand range slightly so points near edges are visible
- PlotUpdater.prototype.expandRange = function () {
- var padding = PADDING_RATIO * this.dimensions[1],
- top;
- padding = Math.max(padding, 1.0);
- top = Math.ceil(this.origin[1] + this.dimensions[1] + padding / 2);
- this.origin[1] = Math.floor(this.origin[1] - padding / 2);
- this.dimensions[1] = top - this.origin[1];
- };
-
- // Update dimensions and origin based on extrema of plots
- PlotUpdater.prototype.updateBounds = function () {
- var bufferArray = this.bufferArray.filter(function (lineBuffer) {
- return lineBuffer.getLength() > 0; // Ignore empty lines
- }),
- priorDomainOrigin = this.origin[0],
- priorDomainDimensions = this.dimensions[0];
-
- if (bufferArray.length > 0) {
- this.domainExtrema = bufferArray.map(function (lineBuffer) {
- return lineBuffer.getDomainExtrema();
- }).reduce(reduceExtrema);
-
- this.rangeExtrema = bufferArray.map(function (lineBuffer) {
- return lineBuffer.getRangeExtrema();
- }).reduce(reduceExtrema);
-
- // Calculate best-fit dimensions
- this.dimensions = [this.domainExtrema, this.rangeExtrema]
- .map(dimensionsOf);
- this.origin = [this.domainExtrema, this.rangeExtrema]
- .map(originOf);
-
- // Enforce some minimum visible area
- this.expandRange();
-
- // Suppress domain changes when pinned
- if (this.hasSpecificDomainBounds) {
- this.origin[0] = priorDomainOrigin;
- this.dimensions[0] = priorDomainDimensions;
- if (this.following) {
- this.origin[0] = Math.max(
- this.domainExtrema[1] - this.dimensions[0],
- this.origin[0]
- );
- }
- }
-
- // ...then enforce a fixed duration if needed
- if (this.fixedDuration !== undefined) {
- this.origin[0] = this.origin[0] + this.dimensions[0] -
- this.fixedDuration;
- this.dimensions[0] = this.fixedDuration;
- }
- }
- };
-
- // Add latest data for this domain object
- PlotUpdater.prototype.addPointFor = function (domainObject) {
- var line = this.lines[domainObject.getId()];
- if (line) {
- line.addPoint(
- this.handle.getDomainValue(domainObject, this.domain),
- this.handle.getRangeValue(domainObject, this.range)
- );
- }
- };
-
- /**
- * Update with latest data.
- */
- PlotUpdater.prototype.update = function update() {
- var objects = this.handle.getTelemetryObjects(),
- self = this;
-
- // Initialize domain offset if necessary
- if (this.domainOffset === undefined) {
- this.initializeDomainOffset(objects.map(function (obj) {
- return self.handle.getDomainValue(obj, self.domain);
- }).filter(function (value) {
- return typeof value === 'number';
- }));
- }
-
- // Make sure lines are available
- this.prepareLines(objects);
-
- // Add new data
- objects.forEach(function (domainObject, index) {
- self.addPointFor(domainObject, index);
- });
-
- // Then, update extrema
- this.updateBounds();
- };
-
- /**
- * Get the dimensions which bound all data in the provided
- * data sets. This is given as a two-element array where the
- * first element is domain, and second is range.
- * @returns {number[]} the dimensions which bound this data set
- */
- PlotUpdater.prototype.getDimensions = function () {
- return this.dimensions;
- };
-
- /**
- * Get the origin of this data set's boundary.
- * This is given as a two-element array where the
- * first element is domain, and second is range.
- * The domain value here is not adjusted by the domain offset.
- * @returns {number[]} the origin of this data set's boundary
- */
- PlotUpdater.prototype.getOrigin = function () {
- return this.origin;
- };
-
- /**
- * Get the domain offset; this offset will have been subtracted
- * from all domain values in all buffers returned by this
- * preparer, in order to minimize loss-of-precision due to
- * conversion to the 32-bit float format needed by WebGL.
- * @returns {number} the domain offset
- * @memberof platform/features/plot.PlotUpdater#
- */
- PlotUpdater.prototype.getDomainOffset = function () {
- return this.domainOffset;
- };
-
- /**
- * Get all renderable buffers for this data set. This will
- * be returned as an array which can be correlated back to
- * the provided telemetry data objects (from the constructor
- * call) by index.
- *
- * Internally, these are flattened; each buffer contains a
- * sequence of alternating domain and range values.
- *
- * All domain values in all buffers will have been adjusted
- * from their original values by subtraction of the domain
- * offset; this minimizes loss-of-precision resulting from
- * the conversion to 32-bit floats, which may otherwise
- * cause aliasing artifacts (particularly for timestamps)
- *
- * @returns {Float32Array[]} the buffers for these traces
- * @memberof platform/features/plot.PlotUpdater#
- */
- PlotUpdater.prototype.getLineBuffers = function () {
- return this.bufferArray;
- };
-
- /**
- * Set the start and end boundaries (usually time) for the
- * domain axis of this updater.
- */
- PlotUpdater.prototype.setDomainBounds = function (start, end) {
- this.fixedDuration = end - start;
- this.origin[0] = start;
- this.dimensions[0] = this.fixedDuration;
-
- // Suppress follow behavior if we have windowed in on the past
- this.hasSpecificDomainBounds = true;
- this.following =
- !this.domainExtrema || (end >= this.domainExtrema[1]);
- };
-
- /**
- * Fill in historical data.
- */
- PlotUpdater.prototype.addHistorical = function (domainObject, series) {
- var count = series ? series.getPointCount() : 0,
- line;
-
- // Nothing to do if it's an empty series
- if (count < 1) {
- return;
- }
-
- // Initialize domain offset if necessary
- if (this.domainOffset === undefined) {
- this.initializeDomainOffset([
- series.getDomainValue(0, this.domain),
- series.getDomainValue(count - 1, this.domain)
- ]);
- }
-
- // Make sure lines are available
- this.prepareLines(this.handle.getTelemetryObjects());
-
- // Look up the line for this domain object
- line = this.lines[domainObject.getId()];
-
- // ...and put the data into it.
- if (line) {
- line.addSeries(series, this.domain, this.range);
- }
-
- // Update extrema
- this.updateBounds();
- };
-
- return PlotUpdater;
-
- }
-);
-
diff --git a/platform/features/plot/src/modes/PlotModeOptions.js b/platform/features/plot/src/modes/PlotModeOptions.js
deleted file mode 100644
index 671adab6d4..0000000000
--- a/platform/features/plot/src/modes/PlotModeOptions.js
+++ /dev/null
@@ -1,155 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["./PlotOverlayMode", "./PlotStackMode"],
- function (PlotOverlayMode, PlotStackMode) {
-
- var STACKED = {
- key: "stacked",
- name: "Stacked",
- cssClass: "icon-plot-stacked",
- Constructor: PlotStackMode
- },
- OVERLAID = {
- key: "overlaid",
- name: "Overlaid",
- cssClass: "icon-plot-overlay",
- Constructor: PlotOverlayMode
- };
-
- /**
- * Handles distinct behavior associated with different
- * plot modes.
- *
- * @interface platform/features/plot.PlotModeHandler
- * @private
- */
-
- /**
- * Plot telemetry to the sub-plot(s) managed by this mode.
- * @param {platform/features/plot.PlotUpdater} updater a source
- * of data that is ready to plot
- * @method platform/features/plot.PlotModeHandler#plotTelemetry
- */
- /**
- * Get all sub-plots to be displayed in this mode; used
- * to populate the plot template.
- * @return {platform/features/plot.SubPlot[]} all sub-plots to
- * display in this mode
- * @method platform/features/plot.PlotModeHandler#getSubPlots
- */
- /**
- * Check if we are not in our base pan-zoom state (that is,
- * there are some temporary user modifications to the
- * current pan-zoom state.)
- * @returns {boolean} true if not in the base pan-zoom state
- * @method platform/features/plot.PlotModeHandler#isZoomed
- */
- /**
- * Undo the most recent pan/zoom change and restore
- * the prior state.
- * @method platform/features/plot.PlotModeHandler#stepBackPanZoom
- */
- /**
- * Undo all pan/zoom change and restore the base state.
- * @method platform/features/plot.PlotModeHandler#unzoom
- */
-
- /**
- * Determines which plotting modes (stacked/overlaid)
- * are applicable in a given plot view, maintains current
- * selection state thereof, and provides handlers for the
- * different behaviors associated with these modes.
- * @memberof platform/features/plot
- * @constructor
- * @param {DomainObject[]} telemetryObjects the telemetry objects being
- * represented in this plot view
- * @param {platform/features/plot.SubPlotFactory} subPlotFactory a
- * factory for creating sub-plots
- */
- function PlotModeOptions(telemetryObjects, subPlotFactory) {
- this.options = telemetryObjects.length > 1 ?
- [OVERLAID, STACKED] : [OVERLAID];
- this.mode = this.options[0]; // Initial selection (overlaid)
- this.telemetryObjects = telemetryObjects;
- this.subPlotFactory = subPlotFactory;
- }
-
- /**
- * Get a handler for the current mode. This will handle
- * plotting telemetry, providing subplots for the template,
- * and view-level interactions with pan-zoom state.
- * @returns {PlotOverlayMode|PlotStackMode} a handler
- * for the current mode
- */
- PlotModeOptions.prototype.getModeHandler = function () {
- // Lazily initialize
- if (!this.modeHandler) {
- this.modeHandler = new this.mode.Constructor(
- this.telemetryObjects,
- this.subPlotFactory
- );
- }
- return this.modeHandler;
- };
-
- /**
- * Get all mode options available for each plot. Each
- * mode contains a `name` and `cssClass` field suitable
- * for display in a template.
- * @return {Array} the available modes
- */
- PlotModeOptions.prototype.getModeOptions = function () {
- return this.options;
- };
-
- /**
- * Get the plotting mode option currently in use.
- * This will be one of the elements returned from
- * `getModeOptions`.
- * @return {*} the current mode
- */
- PlotModeOptions.prototype.getMode = function () {
- return this.mode;
- };
-
- /**
- * Set the plotting mode option to use.
- * The passed argument must be one of the options
- * returned by `getModeOptions`.
- * @param {object} option one of the plot mode options
- * from `getModeOptions`
- */
- PlotModeOptions.prototype.setMode = function (option) {
- if (this.mode !== option) {
- this.mode = option;
- // Clear the existing mode handler, so it
- // can be instantiated next time it's needed.
- this.modeHandler = undefined;
- }
- };
-
-
- return PlotModeOptions;
- }
-);
diff --git a/platform/features/plot/src/modes/PlotOverlayMode.js b/platform/features/plot/src/modes/PlotOverlayMode.js
deleted file mode 100644
index 993a58d376..0000000000
--- a/platform/features/plot/src/modes/PlotOverlayMode.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["../SubPlot", "../elements/PlotPalette", "../elements/PlotPanZoomStack"],
- function (SubPlot, PlotPalette, PlotPanZoomStack) {
-
- /**
- * Handles plotting in Overlaid mode. In overlaid mode, there
- * is one sub-plot which contains all plotted objects.
- * @memberof platform/features/plot
- * @constructor
- * @implements {platform/features/plot.PlotModeHandler}
- * @param {DomainObject[]} the domain objects to be plotted
- */
- function PlotOverlayMode(telemetryObjects, subPlotFactory) {
- this.panZoomStack = new PlotPanZoomStack([], []);
- this.subplot = subPlotFactory.createSubPlot(
- telemetryObjects,
- this.panZoomStack
- );
- this.subplots = [this.subplot];
- }
-
- PlotOverlayMode.prototype.plotTelemetry = function (updater) {
- // Fit to the boundaries of the data, but don't
- // override any user-initiated pan-zoom changes.
- this.panZoomStack.setBasePanZoom(
- updater.getOrigin(),
- updater.getDimensions()
- );
-
- // Track the domain offset, used to bias domain values
- // to minimize loss of precision when converted to 32-bit
- // floating point values for display.
- this.subplot.setDomainOffset(updater.getDomainOffset());
-
- // Draw the buffers. Select color by index.
- this.subplot.getDrawingObject().lines =
- updater.getLineBuffers().map(function (buf, i) {
- return {
- buffer: buf.getBuffer(),
- color: PlotPalette.getFloatColor(i),
- points: buf.getLength()
- };
- });
- };
-
- PlotOverlayMode.prototype.getSubPlots = function () {
- return this.subplots;
- };
-
- PlotOverlayMode.prototype.isZoomed = function () {
- return this.panZoomStack.getDepth() > 1;
- };
-
- PlotOverlayMode.prototype.stepBackPanZoom = function () {
- this.panZoomStack.popPanZoom();
- this.subplot.update();
- };
-
- PlotOverlayMode.prototype.unzoom = function () {
- this.panZoomStack.clearPanZoom();
- this.subplot.update();
- };
-
- return PlotOverlayMode;
- }
-);
diff --git a/platform/features/plot/src/modes/PlotStackMode.js b/platform/features/plot/src/modes/PlotStackMode.js
deleted file mode 100644
index 8363c032bc..0000000000
--- a/platform/features/plot/src/modes/PlotStackMode.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["../SubPlot", "../elements/PlotPalette", "../elements/PlotPanZoomStackGroup"],
- function (SubPlot, PlotPalette, PlotPanZoomStackGroup) {
-
- /**
- * Handles plotting in Stacked mode. In stacked mode, there
- * is one sub-plot for each plotted object.
- * @memberof platform/features/plot
- * @constructor
- * @implements {platform/features/plot.PlotModeHandler}
- * @param {DomainObject[]} the domain objects to be plotted
- */
- function PlotStackMode(telemetryObjects, subPlotFactory) {
- var self = this;
-
- this.panZoomStackGroup =
- new PlotPanZoomStackGroup(telemetryObjects.length);
-
- this.subplots = telemetryObjects.map(function (telemetryObject, i) {
- return subPlotFactory.createSubPlot(
- [telemetryObject],
- self.panZoomStackGroup.getPanZoomStack(i)
- );
- });
- }
-
- PlotStackMode.prototype.plotTelemetryTo = function (subplot, prepared, index) {
- var buffer = prepared.getLineBuffers()[index];
-
- // Track the domain offset, used to bias domain values
- // to minimize loss of precision when converted to 32-bit
- // floating point values for display.
- subplot.setDomainOffset(prepared.getDomainOffset());
-
- // Draw the buffers. Always use the 0th color, because there
- // is one line per plot.
- subplot.getDrawingObject().lines = [{
- buffer: buffer.getBuffer(),
- color: PlotPalette.getFloatColor(0),
- points: buffer.getLength()
- }];
- };
-
- PlotStackMode.prototype.plotTelemetry = function (prepared) {
- var self = this;
- // Fit to the boundaries of the data, but don't
- // override any user-initiated pan-zoom changes.
- this.panZoomStackGroup.setBasePanZoom(
- prepared.getOrigin(),
- prepared.getDimensions()
- );
-
- this.subplots.forEach(function (subplot, index) {
- self.plotTelemetryTo(subplot, prepared, index);
- });
- };
-
- PlotStackMode.prototype.getSubPlots = function () {
- return this.subplots;
- };
-
- PlotStackMode.prototype.isZoomed = function () {
- return this.panZoomStackGroup.getDepth() > 1;
- };
-
- PlotStackMode.prototype.stepBackPanZoom = function () {
- this.panZoomStackGroup.popPanZoom();
- this.subplots.forEach(function (subplot) {
- subplot.update();
- });
- };
-
- PlotStackMode.prototype.unzoom = function () {
- this.panZoomStackGroup.clearPanZoom();
- this.subplots.forEach(function (subplot) {
- subplot.update();
- });
- };
-
- return PlotStackMode;
- }
-);
diff --git a/platform/features/plot/test/Canvas2DChartSpec.js b/platform/features/plot/test/Canvas2DChartSpec.js
deleted file mode 100644
index 28fe85b088..0000000000
--- a/platform/features/plot/test/Canvas2DChartSpec.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/Canvas2DChart"],
- function (Canvas2DChart) {
-
- describe("A canvas 2d chart", function () {
- var mockCanvas,
- mock2d,
- chart;
-
- beforeEach(function () {
- mockCanvas = jasmine.createSpyObj("canvas", ["getContext"]);
- mock2d = jasmine.createSpyObj(
- "2d",
- [
- "clearRect",
- "beginPath",
- "moveTo",
- "lineTo",
- "stroke",
- "fillRect"
- ]
- );
- mockCanvas.getContext.andReturn(mock2d);
-
- chart = new Canvas2DChart(mockCanvas);
- });
-
- // Note that tests below are less specific than they
- // could be, esp. w.r.t. arguments to drawing calls;
- // this is a fallback option so is a lower test priority.
-
- it("allows the canvas to be cleared", function () {
- chart.clear();
- expect(mock2d.clearRect).toHaveBeenCalled();
- });
-
- it("does not construct if 2D is unavailable", function () {
- mockCanvas.getContext.andReturn(undefined);
- expect(function () {
- return new Canvas2DChart(mockCanvas);
- }).toThrow();
- });
-
- it("allows dimensions to be set", function () {
- // No return value, just verify API is present
- chart.setDimensions([120, 120], [0, 10]);
- });
-
- it("allows lines to be drawn", function () {
- var testBuffer = [0, 1, 3, 8],
- testColor = [0.25, 0.33, 0.66, 1.0],
- testPoints = 2;
- chart.drawLine(testBuffer, testColor, testPoints);
- expect(mock2d.beginPath).toHaveBeenCalled();
- expect(mock2d.lineTo.calls.length).toEqual(1);
- expect(mock2d.stroke).toHaveBeenCalled();
- });
-
- it("allows squares to be drawn", function () {
- var testMin = [0, 1],
- testMax = [10, 10],
- testColor = [0.25, 0.33, 0.66, 1.0];
-
- chart.drawSquare(testMin, testMax, testColor);
- expect(mock2d.fillRect).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/GLChartSpec.js b/platform/features/plot/test/GLChartSpec.js
deleted file mode 100644
index cd9afcb91f..0000000000
--- a/platform/features/plot/test/GLChartSpec.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/GLChart"],
- function (GLChart) {
-
- describe("A WebGL chart", function () {
- var mockCanvas,
- mockGL,
- glChart;
-
- beforeEach(function () {
- mockCanvas = jasmine.createSpyObj("canvas", ["getContext"]);
- mockGL = jasmine.createSpyObj(
- "gl",
- [
- "createShader",
- "compileShader",
- "shaderSource",
- "attachShader",
- "createProgram",
- "linkProgram",
- "useProgram",
- "enableVertexAttribArray",
- "getAttribLocation",
- "getUniformLocation",
- "createBuffer",
- "lineWidth",
- "enable",
- "blendFunc",
- "viewport",
- "clear",
- "uniform2fv",
- "uniform4fv",
- "bufferData",
- "bindBuffer",
- "vertexAttribPointer",
- "drawArrays"
- ]
- );
- mockGL.ARRAY_BUFFER = "ARRAY_BUFFER";
- mockGL.DYNAMIC_DRAW = "DYNAMIC_DRAW";
- mockGL.TRIANGLE_FAN = "TRIANGLE_FAN";
- mockGL.LINE_STRIP = "LINE_STRIP";
-
- // Echo back names for uniform locations, so we can
- // test which of these are set for certain operations.
- mockGL.getUniformLocation.andCallFake(function (a, name) {
- return name;
- });
-
- mockCanvas.getContext.andReturn(mockGL);
-
- glChart = new GLChart(mockCanvas);
- });
-
- it("allows the canvas to be cleared", function () {
- glChart.clear();
- expect(mockGL.clear).toHaveBeenCalled();
- });
-
- it("does not construct if WebGL is unavailable", function () {
- mockCanvas.getContext.andReturn(undefined);
- expect(function () {
- return new GLChart(mockCanvas);
- }).toThrow();
- });
-
- it("allows dimensions to be set", function () {
- glChart.setDimensions([120, 120], [0, 10]);
- expect(mockGL.uniform2fv)
- .toHaveBeenCalledWith("uDimensions", [120, 120]);
- expect(mockGL.uniform2fv)
- .toHaveBeenCalledWith("uOrigin", [0, 10]);
- });
-
- it("allows lines to be drawn", function () {
- var testBuffer = [0, 1, 3, 8],
- testColor = [0.25, 0.33, 0.66, 1.0],
- testPoints = 2;
- glChart.drawLine(testBuffer, testColor, testPoints);
- expect(mockGL.bufferData).toHaveBeenCalledWith(
- mockGL.ARRAY_BUFFER,
- testBuffer,
- mockGL.DYNAMIC_DRAW
- );
- expect(mockGL.uniform4fv)
- .toHaveBeenCalledWith("uColor", testColor);
- expect(mockGL.drawArrays)
- .toHaveBeenCalledWith("LINE_STRIP", 0, testPoints);
- });
-
- it("allows squares to be drawn", function () {
- var testMin = [0, 1],
- testMax = [10, 10],
- testColor = [0.25, 0.33, 0.66, 1.0];
-
- glChart.drawSquare(testMin, testMax, testColor);
-
- expect(mockGL.uniform4fv)
- .toHaveBeenCalledWith("uColor", testColor);
- expect(mockGL.drawArrays)
- .toHaveBeenCalledWith("TRIANGLE_FAN", 0, 4);
- });
-
- it("uses buffer sizes reported by WebGL", function () {
- // Make sure that GLChart uses the GL buffer size, which may
- // differ from what canvas requested. WTD-852
- mockCanvas.width = 300;
- mockCanvas.height = 150;
- mockGL.drawingBufferWidth = 200;
- mockGL.drawingBufferHeight = 175;
-
- glChart.clear();
-
- expect(mockGL.viewport).toHaveBeenCalledWith(0, 0, 200, 175);
- });
- });
- }
-);
diff --git a/platform/features/plot/test/MCTChartSpec.js b/platform/features/plot/test/MCTChartSpec.js
deleted file mode 100644
index 2beb5f2a6a..0000000000
--- a/platform/features/plot/test/MCTChartSpec.js
+++ /dev/null
@@ -1,216 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/MCTChart"],
- function (MCTChart) {
-
- describe("The mct-chart directive", function () {
- var mockInterval,
- mockLog,
- mockScope,
- mockElement,
- mockCanvas,
- mockGL,
- mockC2d,
- mockPromise,
- mctChart;
-
- beforeEach(function () {
- mockInterval =
- jasmine.createSpy("$interval");
- mockLog =
- jasmine.createSpyObj("$log", ["warn", "info", "debug"]);
- mockScope = jasmine.createSpyObj(
- "$scope",
- ["$watchCollection", "$on", "$apply"]
- );
- mockElement =
- jasmine.createSpyObj("element", ["find", "html"]);
- mockInterval.cancel = jasmine.createSpy("cancelInterval");
- mockPromise = jasmine.createSpyObj("promise", ["then"]);
-
-
- // mct-chart uses GLChart, so it needs WebGL API
- mockCanvas =
- jasmine.createSpyObj("canvas", ["getContext", "addEventListener"]);
- mockGL = jasmine.createSpyObj(
- "gl",
- [
- "createShader",
- "compileShader",
- "shaderSource",
- "attachShader",
- "createProgram",
- "linkProgram",
- "useProgram",
- "enableVertexAttribArray",
- "getAttribLocation",
- "getUniformLocation",
- "createBuffer",
- "lineWidth",
- "enable",
- "blendFunc",
- "viewport",
- "clear",
- "uniform2fv",
- "uniform4fv",
- "bufferData",
- "bindBuffer",
- "vertexAttribPointer",
- "drawArrays"
- ]
- );
- mockC2d = jasmine.createSpyObj('c2d', ['clearRect']);
- mockGL.ARRAY_BUFFER = "ARRAY_BUFFER";
- mockGL.DYNAMIC_DRAW = "DYNAMIC_DRAW";
- mockGL.TRIANGLE_FAN = "TRIANGLE_FAN";
- mockGL.LINE_STRIP = "LINE_STRIP";
-
- // Echo back names for uniform locations, so we can
- // test which of these are set for certain operations.
- mockGL.getUniformLocation.andCallFake(function (a, name) {
- return name;
- });
-
- mockElement.find.andReturn([mockCanvas]);
- mockCanvas.getContext.andCallFake(function (type) {
- return { webgl: mockGL, '2d': mockC2d }[type];
- });
- mockInterval.andReturn(mockPromise);
-
- mctChart = new MCTChart(mockInterval, mockLog);
- });
-
- it("is applicable at the element level", function () {
- expect(mctChart.restrict).toEqual("E");
- });
-
- it("places a 'draw' attribute in-scope", function () {
- // Should ask Angular for the draw attribute
- expect(mctChart.scope.draw).toEqual("=");
- });
-
- it("watches for changes in the drawn object", function () {
- mctChart.link(mockScope, mockElement);
- expect(mockScope.$watchCollection)
- .toHaveBeenCalledWith("draw", jasmine.any(Function));
- });
-
- it("issues one draw call per line", function () {
- mctChart.link(mockScope, mockElement);
- mockScope.$watchCollection.mostRecentCall.args[1]({
- lines: [{}, {}, {}]
- });
- expect(mockGL.drawArrays.calls.length).toEqual(3);
- });
-
- it("issues one draw call per box", function () {
- mctChart.link(mockScope, mockElement);
- mockScope.$watchCollection.mostRecentCall.args[1]({
- boxes: [
- { start: [0, 0], end: [1, 1] },
- { start: [0, 0], end: [1, 1] },
- { start: [0, 0], end: [1, 1] },
- { start: [0, 0], end: [1, 1] }
- ]
- });
- expect(mockGL.drawArrays.calls.length).toEqual(4);
- });
-
- it("does not fail if no draw object is in scope", function () {
- mctChart.link(mockScope, mockElement);
- expect(mockScope.$watchCollection.mostRecentCall.args[1])
- .not.toThrow();
- });
-
- it("draws on canvas resize", function () {
- mctChart.link(mockScope, mockElement);
-
- // Should track canvas size in an interval
- expect(mockInterval).toHaveBeenCalledWith(
- jasmine.any(Function),
- jasmine.any(Number),
- 0,
- false
- );
-
- // Verify pre-condition
- expect(mockGL.clear).not.toHaveBeenCalled();
-
- mockCanvas.width = 100;
- mockCanvas.offsetWidth = 150;
- mockCanvas.height = 200;
- mockCanvas.offsetHeight = 200;
- mockInterval.mostRecentCall.args[0]();
-
- // Use clear as an indication that drawing has occurred
- expect(mockGL.clear).toHaveBeenCalled();
- });
-
- it("warns if no WebGL context is available", function () {
- mockCanvas.getContext.andReturn(undefined);
- mctChart.link(mockScope, mockElement);
- expect(mockLog.warn).toHaveBeenCalled();
- });
-
- it("falls back to Canvas 2d API if WebGL context is lost", function () {
- mctChart.link(mockScope, mockElement);
- expect(mockCanvas.addEventListener)
- .toHaveBeenCalledWith("webglcontextlost", jasmine.any(Function));
- expect(mockCanvas.getContext).not.toHaveBeenCalledWith('2d');
- mockCanvas.addEventListener.mostRecentCall.args[1]();
- expect(mockCanvas.getContext).toHaveBeenCalledWith('2d');
- });
-
- it("logs nothing in nominal situations (WebGL available)", function () {
- // Complement the previous test
- mctChart.link(mockScope, mockElement);
- expect(mockLog.warn).not.toHaveBeenCalled();
- });
-
- // Avoid resource leaks
- it("stops polling for size changes on destroy", function () {
- mctChart.link(mockScope, mockElement);
-
- // Should be listening for a destroy event
- expect(mockScope.$on).toHaveBeenCalledWith(
- "$destroy",
- jasmine.any(Function)
- );
-
- // Precondition - interval still active
- expect(mockInterval.cancel).not.toHaveBeenCalled();
-
- // Broadcast a $destroy
- mockScope.$on.mostRecentCall.args[1]();
-
- // Should have stopped the interval
- expect(mockInterval.cancel).toHaveBeenCalledWith(mockPromise);
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/PlotControllerSpec.js b/platform/features/plot/test/PlotControllerSpec.js
deleted file mode 100644
index c8480622db..0000000000
--- a/platform/features/plot/test/PlotControllerSpec.js
+++ /dev/null
@@ -1,403 +0,0 @@
-/*global angular*/
-
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/PlotController"],
- function (PlotController) {
-
- describe("The plot controller", function () {
- var mockScope,
- mockElement,
- mockExportImageService,
- mockFormatter,
- mockHandler,
- mockThrottle,
- mockHandle,
- mockDomainObject,
- mockSeries,
- mockStatusCapability,
- controller,
- mockConductor;
-
- function bind(method, thisObj) {
- return function () {
- return method.apply(thisObj, arguments);
- };
- }
-
- function fireEvent(name, args) {
- mockScope.$on.calls.forEach(function (call) {
- if (call.args[0] === name) {
- call.args[1].apply(null, args || []);
- }
- });
- }
-
- function fireWatch(expr, value) {
- mockScope.$watch.calls.forEach(function (call) {
- if (call.args[0] === expr) {
- call.args[1].apply(null, [value]);
- }
- });
- }
-
-
- beforeEach(function () {
- mockScope = jasmine.createSpyObj(
- "$scope",
- ["$watch", "$on", "$emit"]
- );
- mockElement = angular.element('');
- mockExportImageService = jasmine.createSpyObj(
- "ExportImageService",
- ["exportJPG", "exportPNG"]
- );
- mockFormatter = jasmine.createSpyObj(
- "formatter",
- ["formatDomainValue", "formatRangeValue"]
- );
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability", "hasCapability"]
- );
- mockHandler = jasmine.createSpyObj(
- "telemetrySubscriber",
- ["handle"]
- );
- mockThrottle = jasmine.createSpy("throttle");
- mockHandle = jasmine.createSpyObj(
- "subscription",
- [
- "unsubscribe",
- "getTelemetryObjects",
- "getMetadata",
- "getDomainValue",
- "getRangeValue",
- "getDatum",
- "request"
- ]
- );
- mockSeries = jasmine.createSpyObj(
- 'series',
- ['getPointCount', 'getDomainValue', 'getRangeValue']
- );
-
- mockStatusCapability = jasmine.createSpyObj(
- "statusCapability",
- ["set"]
- );
-
- mockHandler.handle.andReturn(mockHandle);
- mockThrottle.andCallFake(function (fn) {
- return fn;
- });
- mockHandle.getTelemetryObjects.andReturn([mockDomainObject]);
- mockHandle.getMetadata.andReturn([{}]);
- mockHandle.getDomainValue.andReturn(123);
- mockHandle.getRangeValue.andReturn(42);
- mockScope.domainObject = mockDomainObject;
-
- mockConductor = jasmine.createSpyObj('conductor', [
- 'on',
- 'off',
- 'bounds',
- 'timeSystem',
- 'timeOfInterest',
- 'follow'
- ]);
-
- mockConductor.bounds.andReturn({});
-
- controller = new PlotController(
- mockScope,
- mockElement,
- mockExportImageService,
- mockFormatter,
- mockHandler,
- mockThrottle,
- undefined,
- {time: mockConductor}
- );
- });
-
- it("provides plot colors", function () {
- // PlotPalette will have its own tests
- expect(controller.getColor(0))
- .toEqual(jasmine.any(String));
-
- // Colors should be unique
- expect(controller.getColor(0))
- .not.toEqual(controller.getColor(1));
- });
-
- it("subscribes to telemetry when a domain object appears in scope", function () {
- // Make sure we're using the right watch here
- expect(mockScope.$watch.mostRecentCall.args[0])
- .toEqual("domainObject");
- // Make an object available
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- // Should have subscribed
- expect(mockHandler.handle).toHaveBeenCalledWith(
- mockDomainObject,
- jasmine.any(Function),
- true // Lossless
- );
- });
-
- it("draws lines when data becomes available", function () {
- // Make an object available
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
-
- // Verify precondition
- controller.getSubPlots().forEach(function (subplot) {
- expect(subplot.getDrawingObject().lines)
- .not.toBeDefined();
- });
-
- // Make sure there actually are subplots being verified
- expect(controller.getSubPlots().length > 0).toBeTruthy();
-
- // Broadcast data
- mockHandler.handle.mostRecentCall.args[1]();
-
- controller.getSubPlots().forEach(function (subplot) {
- expect(subplot.getDrawingObject().lines)
- .toBeDefined();
- });
- });
-
- it("unsubscribes when domain object changes", function () {
- // Make an object available
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- // Verify precondition - shouldn't unsubscribe yet
- expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
- // Remove the domain object
- mockScope.$watch.mostRecentCall.args[1](undefined);
- // Should have unsubscribed
- expect(mockHandle.unsubscribe).toHaveBeenCalled();
- });
-
-
- it("changes modes depending on number of objects", function () {
- // Act like one object is available
- mockHandle.getTelemetryObjects.andReturn([
- mockDomainObject
- ]);
-
- // Make an object available; invoke handler's callback
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- mockHandler.handle.mostRecentCall.args[1]();
-
- expect(controller.getModeOptions().length).toEqual(1);
-
- // Act like one object is available
- mockHandle.getTelemetryObjects.andReturn([
- mockDomainObject,
- mockDomainObject,
- mockDomainObject
- ]);
-
- // Make an object available; invoke handler's callback
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- mockHandler.handle.mostRecentCall.args[1]();
-
- expect(controller.getModeOptions().length).toEqual(2);
- });
-
- // Interface tests follow; these will be delegated (mostly
- // to PlotModeOptions, which is tested separately).
- it("provides access to available plot mode options", function () {
- expect(Array.isArray(controller.getModeOptions()))
- .toBeTruthy();
- });
-
- it("provides a current plot mode", function () {
- expect(controller.getMode().name)
- .toEqual(jasmine.any(String));
- });
-
- it("allows plot mode to be changed", function () {
- expect(function () {
- controller.setMode(controller.getMode());
- }).not.toThrow();
- });
-
- it("provides an array of sub-plots", function () {
- expect(Array.isArray(controller.getSubPlots()))
- .toBeTruthy();
- });
-
- it("allows plots to be updated", function () {
- expect(bind(controller.update, controller)).not.toThrow();
- });
-
- it("allows changing pan-zoom state", function () {
- expect(bind(controller.isZoomed, controller)).not.toThrow();
- expect(bind(controller.stepBackPanZoom, controller)).not.toThrow();
- expect(bind(controller.unzoom, controller)).not.toThrow();
- });
-
- it("sets status when plot becomes detached from time conductor", function () {
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
-
- function boundsEvent() {
- fireEvent("telemetry:display:bounds", [
- {},
- { start: 10, end: 100 },
- true
- ]);
- }
-
- mockDomainObject.hasCapability.andCallFake(function (name) {
- return name === "status";
- });
- mockDomainObject.getCapability.andReturn(mockStatusCapability);
- spyOn(controller, "isZoomed");
-
- //Mock zoomed in state
- controller.isZoomed.andReturn(true);
- boundsEvent();
- expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", true);
-
- //"Reset" zoom
- controller.isZoomed.andReturn(false);
- boundsEvent();
- expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", false);
- });
-
- it("indicates if a request is pending", function () {
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- expect(controller.isRequestPending()).toBeTruthy();
- mockHandle.request.mostRecentCall.args[1](
- mockDomainObject,
- mockSeries
- );
- expect(controller.isRequestPending()).toBeFalsy();
- });
-
- it("requests historical telemetry", function () {
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- expect(mockHandle.request).toHaveBeenCalled();
- mockHandle.request.mostRecentCall.args[1](
- mockDomainObject,
- mockSeries
- );
- });
-
- it("unsubscribes when destroyed", function () {
- // Make an object available
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- // Make sure $destroy is what's listened for
- expect(mockScope.$on.mostRecentCall.args[0]).toEqual('$destroy');
- // Also verify precondition
- expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
- // Destroy the scope
- fireEvent("$destroy");
- // Should have unsubscribed
- expect(mockHandle.unsubscribe).toHaveBeenCalled();
- });
-
- it("requeries when displayable bounds change", function () {
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- expect(mockHandle.request.calls.length).toEqual(1);
- fireEvent("telemetry:display:bounds", [
- {},
- { start: 10, end: 100 }
- ]);
- expect(mockHandle.request.calls.length).toEqual(2);
- });
-
- it("requeries when user changes domain selection", function () {
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- expect(mockHandle.request.calls.length).toEqual(1);
- fireWatch("axes[0].active.key", 'someNewKey');
- expect(mockHandle.request.calls.length).toEqual(2);
- });
-
- it("requeries when user changes range selection", function () {
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- expect(mockHandle.request.calls.length).toEqual(1);
- fireWatch("axes[1].active.key", 'someNewKey');
- expect(mockHandle.request.calls.length).toEqual(2);
- });
-
- it("maintains externally-provided domain axis bounds after data is received", function () {
- mockSeries.getPointCount.andReturn(3);
- mockSeries.getRangeValue.andReturn(42);
- mockSeries.getDomainValue.andCallFake(function (i) {
- return 2500 + i * 2500;
- });
-
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- fireEvent("telemetry:display:bounds", [
- {},
- {start: 0, end: 10000}
- ]);
- mockHandle.request.mostRecentCall.args[1](
- mockDomainObject,
- mockSeries
- );
-
- // Pan-zoom state should reflect bounds set externally;
- // domain axis should not have shrunk to fit data.
- expect(
- controller.getSubPlots()[0].panZoomStack.getOrigin()[0]
- ).toEqual(0);
- expect(
- controller.getSubPlots()[0].panZoomStack.getDimensions()[0]
- ).toEqual(10000);
- });
-
- it("provides classes for legends based on limit state", function () {
- var mockTelemetryObjects = mockHandle.getTelemetryObjects();
-
- mockHandle.getDatum.andReturn({});
- mockTelemetryObjects.forEach(function (mockObject, i) {
- var id = 'object-' + i,
- mockLimitCapability =
- jasmine.createSpyObj('limit-' + id, ['evaluate']);
-
- mockObject.getId.andReturn(id);
- mockObject.getCapability.andCallFake(function (key) {
- return (key === 'limit') && mockLimitCapability;
- });
-
- mockLimitCapability.evaluate
- .andReturn({ cssClass: 'alarm-' + id });
- });
-
- mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
- mockHandler.handle.mostRecentCall.args[1]();
-
- mockTelemetryObjects.forEach(function (mockTelemetryObject) {
- expect(controller.getLegendClass(mockTelemetryObject))
- .toEqual('alarm-' + mockTelemetryObject.getId());
- });
- });
- });
- }
-);
diff --git a/platform/features/plot/test/PlotOptionsControllerSpec.js b/platform/features/plot/test/PlotOptionsControllerSpec.js
deleted file mode 100644
index 9a409f4cf9..0000000000
--- a/platform/features/plot/test/PlotOptionsControllerSpec.js
+++ /dev/null
@@ -1,147 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ['../src/PlotOptionsController'],
- function (PlotOptionsController) {
-
- describe("The Plot Options controller", function () {
- var plotOptionsController,
- mockDomainObject,
- mockMutationCapability,
- mockUseCapabilities,
- mockCompositionCapability,
- mockComposition,
- mockUnlisten,
- mockChildOne,
- mockChildTwo,
- model,
- mockScope;
-
- beforeEach(function () {
- model = {
- composition: ['childOne']
- };
-
- mockChildOne = jasmine.createSpyObj('domainObject', [
- 'getId'
- ]);
- mockChildOne.getId.andReturn('childOne');
-
- mockChildTwo = jasmine.createSpyObj('childTwo', [
- 'getId'
- ]);
- mockChildOne.getId.andReturn('childTwo');
-
- mockCompositionCapability = jasmine.createSpyObj('compositionCapability', [
- 'then'
- ]);
- mockComposition = [
- mockChildOne
- ];
- mockCompositionCapability.then.andCallFake(function (callback) {
- callback(mockComposition);
- });
-
- mockUseCapabilities = jasmine.createSpyObj('useCapabilities', [
- 'composition',
- 'mutation'
- ]);
- mockUseCapabilities.composition.andReturn(mockCompositionCapability);
-
- mockMutationCapability = jasmine.createSpyObj('mutationCapability', [
- 'listen'
- ]);
- mockUnlisten = jasmine.createSpy('unlisten');
- mockMutationCapability.listen.andReturn(mockUnlisten);
-
- mockDomainObject = jasmine.createSpyObj('domainObject', [
- 'getModel',
- 'useCapability',
- 'getCapability'
- ]);
- mockDomainObject.useCapability.andCallFake(function (capability) {
- return mockUseCapabilities[capability]();
- });
- mockDomainObject.getCapability.andReturn(mockMutationCapability);
- mockDomainObject.getModel.andReturn(model);
-
- mockScope = jasmine.createSpyObj('scope', [
- '$on',
- '$watchCollection'
- ]);
- mockScope.domainObject = mockDomainObject;
-
- function noop() {}
- mockScope.$watchCollection.andReturn(noop);
-
- plotOptionsController = new PlotOptionsController(mockScope);
- });
-
- it("sets form definitions on scope", function () {
- expect(mockScope.xAxisForm).toBeDefined();
- expect(mockScope.yAxisForm).toBeDefined();
- expect(mockScope.plotSeriesForm).toBeDefined();
- });
-
- it("sets object children on scope", function () {
- expect(mockScope.children).toBe(mockComposition);
- });
-
- it("on changes in object composition, updates the form", function () {
- expect(mockMutationCapability.listen).toHaveBeenCalled();
- expect(mockScope.children).toBe(mockComposition);
- expect(mockScope.children.length).toBe(1);
- mockComposition.push(mockChildTwo);
- model.composition.push('childTwo');
- mockMutationCapability.listen.mostRecentCall.args[0](model);
- expect(mockScope.children).toBe(mockComposition);
- expect(mockScope.children.length).toBe(2);
- });
-
- it("on changes in form values, updates the object model", function () {
- var scopeConfiguration = mockScope.configuration,
- objModel = mockDomainObject.getModel();
-
- scopeConfiguration.plot.yAxis.autoScale = true;
- scopeConfiguration.plot.yAxis.key = 'eu';
- scopeConfiguration.plot.xAxis.key = 'lst';
-
- expect(mockScope.$watchCollection).toHaveBeenCalled();
- mockScope.$watchCollection.calls[0].args[1]();
- expect(mockDomainObject.useCapability).toHaveBeenCalledWith('mutation', jasmine.any(Function));
-
- mockDomainObject.useCapability.mostRecentCall.args[1](objModel);
- expect(objModel.configuration.plot.yAxis.autoScale).toBe(true);
- expect(objModel.configuration.plot.yAxis.key).toBe('eu');
- expect(objModel.configuration.plot.xAxis.key).toBe('lst');
-
- });
-
- it("cleans up listeners on destruction of the controller", function () {
- mockScope.$on.mostRecentCall.args[1]();
- expect(mockUnlisten).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/SubPlotFactorySpec.js b/platform/features/plot/test/SubPlotFactorySpec.js
deleted file mode 100644
index 67798f9e07..0000000000
--- a/platform/features/plot/test/SubPlotFactorySpec.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/SubPlotFactory"],
- function (SubPlotFactory) {
-
- describe("The sub-plot factory", function () {
- var mockDomainObject,
- mockPanZoomStack,
- mockFormatter,
- factory;
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability"]
- );
- mockPanZoomStack = jasmine.createSpyObj(
- "panZoomStack",
- ["getPanZoom"]
- );
- mockFormatter = jasmine.createSpyObj(
- "formatter",
- ["formatDomainValue", "formatRangeValue"]
- );
-
- mockPanZoomStack.getPanZoom.andReturn({
- origin: [0, 0],
- dimensions: [100, 100]
- });
-
- factory = new SubPlotFactory(mockFormatter);
- });
-
- it("creates sub-plots", function () {
- expect(factory.createSubPlot(
- [mockDomainObject],
- mockPanZoomStack
- ).getTelemetryObjects()).toEqual([mockDomainObject]);
- });
- });
- }
-);
diff --git a/platform/features/plot/test/SubPlotSpec.js b/platform/features/plot/test/SubPlotSpec.js
deleted file mode 100644
index ff38f9b2ca..0000000000
--- a/platform/features/plot/test/SubPlotSpec.js
+++ /dev/null
@@ -1,208 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../src/SubPlot"],
- function (SubPlot) {
-
- describe("A sub-plot", function () {
- var mockDomainObject,
- mockPanZoomStack,
- mockFormatter,
- mockElement,
- testDomainObjects,
- testOrigin,
- testDimensions,
- subplot;
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability"]
- );
- mockPanZoomStack = jasmine.createSpyObj(
- "panZoomStack",
- [
- "getDepth",
- "pushPanZoom",
- "popPanZoom",
- "setBasePanZoom",
- "clearPanZoom",
- "getPanZoom",
- "getOrigin",
- "getDimensions"
- ]
- );
- mockFormatter = jasmine.createSpyObj(
- "formatter",
- ["formatDomainValue", "formatRangeValue"]
- );
- mockElement = jasmine.createSpyObj(
- "element",
- ["getBoundingClientRect"]
- );
-
- testOrigin = [5, 10];
- testDimensions = [3000, 1000];
- testDomainObjects = [mockDomainObject, mockDomainObject];
-
- mockPanZoomStack.getOrigin.andReturn(testOrigin);
- mockPanZoomStack.getDimensions.andReturn(testDimensions);
- mockPanZoomStack.getPanZoom.andReturn(
- { origin: testOrigin, dimensions: testDimensions }
- );
- mockElement.getBoundingClientRect.andReturn(
- { left: 10, top: 20, width: 100, height: 100 }
- );
-
- subplot = new SubPlot(
- testDomainObjects,
- mockPanZoomStack,
- mockFormatter
- );
- });
-
-
- it("provides a getter for its plotted objects", function () {
- expect(subplot.getTelemetryObjects())
- .toEqual(testDomainObjects);
- });
-
- it("exposes tick marks", function () {
- // Just test availability; details are tested
- // in PlotTickFormatter
- expect(Array.isArray(subplot.getDomainTicks()))
- .toBeTruthy();
- expect(Array.isArray(subplot.getRangeTicks()))
- .toBeTruthy();
- });
-
- it("allows hovering state to be tracked", function () {
- expect(subplot.isHovering()).toBeFalsy();
- expect(subplot.isHovering(true)).toBeTruthy();
- expect(subplot.isHovering()).toBeTruthy();
- expect(subplot.isHovering(false)).toBeFalsy();
- expect(subplot.isHovering()).toBeFalsy();
- });
-
- it("provides hovering coordinates", function () {
- // Should be empty when not hovering
- expect(subplot.getHoverCoordinates())
- .toBeUndefined();
-
- // Start hovering
- subplot.hover({ target: mockElement });
-
- // Should now have coordinates to display
- expect(subplot.getHoverCoordinates())
- .toEqual(jasmine.any(String));
- });
-
- it("supports marquee zoom", function () {
- expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
-
- // Simulate a marquee zoom. Note that the mockElement
- // is 100 by 100 and starts at 10,20
- subplot.startDrag({
- target: mockElement,
- clientX: 60,
- clientY: 45
- });
- subplot.hover({
- target: mockElement,
- clientX: 75,
- clientY: 85
- });
- subplot.endDrag({
- target: mockElement,
- clientX: 80,
- clientY: 95
- });
- // ... so the origin should be 50%,25% into current dimensions,
- // and new dimensions should be 20%,50% thereof
-
- expect(mockPanZoomStack.pushPanZoom).toHaveBeenCalledWith(
- [
- testOrigin[0] + testDimensions[0] * 0.50,
- testOrigin[1] + testDimensions[1] * 0.25
- ],
- [
- testDimensions[0] * 0.20,
- testDimensions[1] * 0.50
- ]
- );
- });
-
- it ("indicates when there is domain data shown", function () {
- expect(subplot.hasDomainData()).toEqual(true);
- });
-
- it ("indicates when there is no domain data shown", function () {
- mockPanZoomStack.getDimensions.andReturn([0,0]);
- expect(subplot.hasDomainData()).toEqual(false);
- });
-
- it("disallows marquee zoom when start and end Marquee is at the same position", function () {
- expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
-
- // Simulate a marquee zoom. Note that the mockElement
- // is 100 by 100 and starts at 10,20
- subplot.startDrag({
- target: mockElement,
- clientX: 60,
- clientY: 45
- });
- subplot.hover({
- target: mockElement,
- clientX: 75,
- clientY: 85
- });
- subplot.endDrag({
- target: mockElement,
- clientX: 60,
- clientY: 45
- });
-
- expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
- });
-
- it("provides access to a drawable object", function () {
- expect(typeof subplot.getDrawingObject()).toEqual('object');
- });
-
- it("allows a domain offset to be provided", function () {
- // Domain object is needed to adjust canvas coordinates
- // to avoid loss-of-precision associated with converting
- // to 32 bit floats.
- subplot.setDomainOffset(3);
- subplot.update();
- // Should have adjusted the origin accordingly
- expect(subplot.getDrawingObject().origin[0])
- .toEqual(2);
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotAxisSpec.js b/platform/features/plot/test/elements/PlotAxisSpec.js
deleted file mode 100644
index 72426ad8ef..0000000000
--- a/platform/features/plot/test/elements/PlotAxisSpec.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/elements/PlotAxis"],
- function (PlotAxis) {
-
- describe("A plot axis", function () {
- var testMetadatas,
- testDefault,
- axis;
-
- beforeEach(function () {
- testMetadatas = [
- {
- tests: [
- { key: "t0", name: "T0" },
- { key: "t1", name: "T1" }
- ],
- someKey: "some value"
- },
- {
- tests: [
- { key: "t0", name: "T0" },
- { key: "t2", name: "T2" }
- ]
- },
- {
- tests: [
- { key: "t3", name: "T3" },
- { key: "t4", name: "T4" },
- { key: "t5", name: "T5" },
- { key: "t6", name: "T6" }
- ]
- }
- ];
- testDefault = { key: "test", name: "Test" };
- axis = new PlotAxis("tests", testMetadatas, testDefault);
- });
-
- it("pulls out a list of domain or range options", function () {
- // Should have filtered out duplicates, etc
- expect(axis.options).toEqual([
- { key: "t0", name: "T0" },
- { key: "t1", name: "T1" },
- { key: "t2", name: "T2" },
- { key: "t3", name: "T3" },
- { key: "t4", name: "T4" },
- { key: "t5", name: "T5" },
- { key: "t6", name: "T6" }
- ]);
- });
-
- it("chooses the first option as a default", function () {
- expect(axis.active).toEqual({ key: "t0", name: "T0" });
- });
-
- it("falls back to a provided default if no options are present", function () {
- expect(new PlotAxis("tests", [{}], testDefault).active)
- .toEqual(testDefault);
- });
-
- it("allows options to be chosen by key", function () {
- axis.chooseOption("t3");
- expect(axis.active).toEqual({ key: "t3", name: "T3" });
- });
-
- it("reflects changes to applicable metadata", function () {
- axis.updateMetadata([testMetadatas[1]]);
- expect(axis.options).toEqual([
- { key: "t0", name: "T0" },
- { key: "t2", name: "T2" }
- ]);
- });
-
- it("returns the same array instance for unchanged metadata", function () {
- // ...to avoid triggering extra digest cycles.
- var oldInstance = axis.options;
- axis.updateMetadata(testMetadatas);
- expect(axis.options).toBe(oldInstance);
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotLimitTrackerSpec.js b/platform/features/plot/test/elements/PlotLimitTrackerSpec.js
deleted file mode 100644
index dc1169cb6e..0000000000
--- a/platform/features/plot/test/elements/PlotLimitTrackerSpec.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["../../src/elements/PlotLimitTracker"],
- function (PlotLimitTracker) {
-
- describe("A plot's limit tracker", function () {
- var mockHandle,
- testRange,
- mockTelemetryObjects,
- testData,
- tracker;
-
- beforeEach(function () {
- testRange = "some-range";
- testData = {};
- mockHandle = jasmine.createSpyObj(
- 'handle',
- ['getTelemetryObjects', 'getDatum']
- );
- mockTelemetryObjects = ['a', 'b', 'c'].map(function (id, i) {
- var mockTelemetryObject = jasmine.createSpyObj(
- 'object-' + id,
- ['getId', 'getCapability', 'getModel']
- ),
- mockLimitCapability = jasmine.createSpyObj(
- 'limit-' + id,
- ['evaluate']
- );
- testData[id] = { id: id, value: i };
- mockTelemetryObject.getId.andReturn(id);
- mockTelemetryObject.getCapability.andCallFake(function (key) {
- return key === 'limit' && mockLimitCapability;
- });
- mockLimitCapability.evaluate
- .andReturn({ cssClass: 'alarm-' + id});
- return mockTelemetryObject;
- });
- mockHandle.getTelemetryObjects.andReturn(mockTelemetryObjects);
- mockHandle.getDatum.andCallFake(function (telemetryObject) {
- return testData[telemetryObject.getId()];
- });
-
- tracker = new PlotLimitTracker(mockHandle, testRange);
- });
-
- it("initially provides no limit state", function () {
- mockTelemetryObjects.forEach(function (mockTelemetryObject) {
- expect(tracker.getLegendClass(mockTelemetryObject))
- .toBeUndefined();
- });
- });
-
- describe("when asked to update", function () {
- beforeEach(function () {
- tracker.update();
- });
-
- it("evaluates limits using the limit capability", function () {
- mockTelemetryObjects.forEach(function (mockTelemetryObject) {
- var id = mockTelemetryObject.getId(),
- mockLimit =
- mockTelemetryObject.getCapability('limit');
- expect(mockLimit.evaluate)
- .toHaveBeenCalledWith(testData[id], testRange);
- });
- });
-
- it("exposes legend classes returned by the limit capability", function () {
- mockTelemetryObjects.forEach(function (mockTelemetryObject) {
- var id = mockTelemetryObject.getId();
- expect(tracker.getLegendClass(mockTelemetryObject))
- .toEqual('alarm-' + id);
- });
- });
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotLineBufferSpec.js b/platform/features/plot/test/elements/PlotLineBufferSpec.js
deleted file mode 100644
index 0868c1dfd0..0000000000
--- a/platform/features/plot/test/elements/PlotLineBufferSpec.js
+++ /dev/null
@@ -1,167 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/elements/PlotLineBuffer"],
- function (PlotLineBuffer) {
-
- var TEST_INITIAL_SIZE = 10,
- TEST_MAX_SIZE = 40,
- TEST_DOMAIN_OFFSET = 42;
-
- describe("A plot line buffer", function () {
- var mockSeries,
- testDomainValues,
- testRangeValues,
- buffer;
-
- beforeEach(function () {
- testDomainValues = [1, 3, 7, 9, 14, 15];
- testRangeValues = [8, 0, 3, 9, 8, 11];
- mockSeries = jasmine.createSpyObj(
- "series",
- ['getPointCount', 'getDomainValue', 'getRangeValue']
- );
- mockSeries.getPointCount.andCallFake(function () {
- return testDomainValues.length;
- });
- mockSeries.getDomainValue.andCallFake(function (i) {
- return testDomainValues[i];
- });
- mockSeries.getRangeValue.andCallFake(function (i) {
- return testRangeValues[i];
- });
-
- buffer = new PlotLineBuffer(
- TEST_DOMAIN_OFFSET,
- TEST_INITIAL_SIZE,
- TEST_MAX_SIZE
- );
-
- // Start with some data in there
- buffer.insert(mockSeries, 0);
- });
-
- it("allows insertion of series data", function () {
- // Convert to a regular array for checking.
- // Verify that domain/ranges were interleaved and
- // that domain offset was adjusted for.
- expect(
- Array.prototype.slice.call(buffer.getBuffer()).slice(0, 12)
- ).toEqual([-41, 8, -39, 0, -35, 3, -33, 9, -28, 8, -27, 11]);
- expect(buffer.getLength()).toEqual(6);
- });
-
- it("finds insertion indexes", function () {
- expect(buffer.findInsertionIndex(0)).toEqual(0);
- expect(buffer.findInsertionIndex(2)).toEqual(1);
- expect(buffer.findInsertionIndex(5)).toEqual(2);
- expect(buffer.findInsertionIndex(10)).toEqual(4);
- expect(buffer.findInsertionIndex(14.5)).toEqual(5);
- expect(buffer.findInsertionIndex(20)).toEqual(6);
- });
-
- it("allows insertion in the middle", function () {
- var head = [-41, 8, -39, 0, -35, 3],
- tail = [-33, 9, -28, 8, -27, 11];
- buffer.insert(mockSeries, 3);
- expect(
- Array.prototype.slice.call(buffer.getBuffer()).slice(0, 24)
- ).toEqual(head.concat(head).concat(tail).concat(tail));
- expect(buffer.getLength()).toEqual(12);
- });
-
- it("allows values to be trimmed from the start", function () {
- buffer.trim(2);
- expect(buffer.getLength()).toEqual(4);
- expect(
- Array.prototype.slice.call(buffer.getBuffer()).slice(0, 8)
- ).toEqual([-35, 3, -33, 9, -28, 8, -27, 11]);
- });
-
- it("expands buffer when needed to accommodate more data", function () {
- var i;
-
- // Initial underlying buffer should be twice initial size...
- // (Since each pair will take up two elements)
- expect(buffer.getBuffer().length).toEqual(20);
-
- // Should be able to insert 6 series of 6 points each
- // (After that, we'll hit the test max of 40)
- for (i = 1; i < 15; i += 1) {
- expect(buffer.insertPoint(i * 10, Math.sin(i), i))
- .toBeTruthy();
- }
-
- // Buffer should have expanded in the process
- expect(buffer.getBuffer().length).toEqual(40);
-
- // Push to maximum size just to make sure...
- for (i = 1; i < 150; i += 1) {
- buffer.insertPoint(i * 10, Math.sin(i), i);
- }
-
- expect(buffer.getBuffer().length).toEqual(80);
- });
-
- it("ensures a maximum size", function () {
- var i;
-
- // Should be able to insert 6 series of 6 points each
- // (After that, we'll hit the test max of 40)
- for (i = 1; i < 6; i += 1) {
- expect(buffer.getLength()).toEqual(6 * i);
- expect(buffer.insert(mockSeries, Number.POSITIVE_INFINITY))
- .toBeTruthy();
- }
-
- // Should be maxed out now
- expect(buffer.getLength()).toEqual(36);
- expect(buffer.insert(mockSeries, Number.POSITIVE_INFINITY))
- .toBeFalsy();
- expect(buffer.getLength()).toEqual(36);
-
- });
-
- it("reduces buffer size when space is no longer needed", function () {
- // Check that actual buffer is sized to the initial size
- // (double TEST_INITIAL_SIZE, since two elements are needed per
- // point; one for domain, one for range)
- expect(buffer.getBuffer().length).toEqual(20);
- // Should have 6 elements now... grow to 24
- buffer.insert(mockSeries, Number.POSITIVE_INFINITY);
- buffer.insert(mockSeries, Number.POSITIVE_INFINITY);
- buffer.insert(mockSeries, Number.POSITIVE_INFINITY);
- // This should have doubled the actual buffer size
- expect(buffer.getBuffer().length).toEqual(80);
- // Remove some values
- buffer.trim(20);
- // Actual buffer size should have been reduced accordingly
- expect(buffer.getBuffer().length).toBeLessThan(80);
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotLineSpec.js b/platform/features/plot/test/elements/PlotLineSpec.js
deleted file mode 100644
index e8f92bdc7c..0000000000
--- a/platform/features/plot/test/elements/PlotLineSpec.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["../../src/elements/PlotLine"],
- function (PlotLine) {
-
- describe("A plot line", function () {
- var mockBuffer,
- mockSeries,
- testDomainBuffer,
- testRangeBuffer,
- testSeries,
- line;
-
- beforeEach(function () {
- testDomainBuffer = [];
- testRangeBuffer = [];
- testSeries = [];
-
- mockBuffer = jasmine.createSpyObj(
- 'buffer',
- ['findInsertionIndex', 'insert', 'insertPoint', 'trim']
- );
- mockSeries = jasmine.createSpyObj(
- 'series',
- ['getPointCount', 'getDomainValue', 'getRangeValue']
- );
-
- mockSeries.getPointCount.andCallFake(function () {
- return testSeries.length;
- });
- mockSeries.getDomainValue.andCallFake(function (i) {
- return (testSeries[i] || [])[0];
- });
- mockSeries.getRangeValue.andCallFake(function (i) {
- return (testSeries[i] || [])[1];
- });
-
- // Function like PlotLineBuffer, to aid in testability
- mockBuffer.findInsertionIndex.andCallFake(function (v) {
- var index = 0;
- if (testDomainBuffer.indexOf(v) !== -1) {
- return -1;
- }
- while ((index < testDomainBuffer.length) &&
- (testDomainBuffer[index] < v)) {
- index += 1;
- }
- return index;
- });
- mockBuffer.insert.andCallFake(function (series, index) {
- var domains = [], ranges = [], i;
- for (i = 0; i < series.getPointCount(); i += 1) {
- domains.push(series.getDomainValue(i));
- ranges.push(series.getRangeValue(i));
- }
- testDomainBuffer = testDomainBuffer.slice(0, index)
- .concat(domains)
- .concat(testDomainBuffer.slice(index));
- testRangeBuffer = testRangeBuffer.slice(0, index)
- .concat(ranges)
- .concat(testRangeBuffer.slice(index));
- return true;
- });
- mockBuffer.insertPoint.andCallFake(function (dv, rv, index) {
- testDomainBuffer.splice(index, 0, dv);
- testRangeBuffer.splice(index, 0, rv);
- return true;
- });
-
- line = new PlotLine(mockBuffer);
- });
-
- it("allows single point insertion", function () {
- line.addPoint(100, 200);
- line.addPoint(50, 42);
- line.addPoint(150, 12321);
- // Should have managed insertion index choices to get to...
- expect(testDomainBuffer).toEqual([50, 100, 150]);
- expect(testRangeBuffer).toEqual([42, 200, 12321]);
- });
-
- it("allows series insertion", function () {
- testSeries = [[50, 42], [100, 200], [150, 12321]];
- line.addSeries(mockSeries);
- // Should have managed insertion index choices to get to...
- expect(testDomainBuffer).toEqual([50, 100, 150]);
- expect(testRangeBuffer).toEqual([42, 200, 12321]);
- });
-
- it("splits series insertion when necessary", function () {
- testSeries = [[50, 42], [100, 200], [150, 12321]];
- line.addPoint(75, 1);
- line.addSeries(mockSeries);
- // Should have managed insertion index choices to get to...
- expect(testDomainBuffer).toEqual([50, 75, 100, 150]);
- expect(testRangeBuffer).toEqual([42, 1, 200, 12321]);
- });
-
- it("attempts to remove points when insertion fails", function () {
- // Verify precondition - normally doesn't try to trim
- line.addPoint(1, 2);
- expect(mockBuffer.trim).not.toHaveBeenCalled();
-
- // But if insertPoint fails, it should trim
- mockBuffer.insertPoint.andReturn(false);
- line.addPoint(2, 3);
- expect(mockBuffer.trim).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotPaletteSpec.js b/platform/features/plot/test/elements/PlotPaletteSpec.js
deleted file mode 100644
index 7bfff18f49..0000000000
--- a/platform/features/plot/test/elements/PlotPaletteSpec.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/elements/PlotPalette"],
- function (PlotPalette) {
-
- describe("The plot palette", function () {
- it("can be used as a constructor", function () {
- // PlotPalette has all static methods, so make
- // sure it returns itself if used as a constructor.
- expect(new PlotPalette()).toBe(PlotPalette);
- });
-
- it("has 30 unique colors in an integer format", function () {
- // Integer format may be useful internal to the application.
- // RGB 0-255
- var i, j;
-
- // Used to verify one of R, G, B in loop below
- function verifyChannel(c) {
- expect(typeof c).toEqual("number");
- expect(c <= 255).toBeTruthy();
- expect(c >= 0).toBeTruthy();
- }
-
- for (i = 0; i < 30; i += 1) {
- // Verify that we got an array of numbers
- expect(Array.isArray(PlotPalette.getIntegerColor(i)))
- .toBeTruthy();
- expect(PlotPalette.getIntegerColor(i).length).toEqual(3);
-
- // Verify all three channels for type and range
- PlotPalette.getIntegerColor(i).forEach(verifyChannel);
-
- // Verify uniqueness
- for (j = i + 1; j < 30; j += 1) {
- expect(PlotPalette.getIntegerColor(i)).not.toEqual(
- PlotPalette.getIntegerColor(j)
- );
- }
- }
- });
-
-
- it("has 30 unique colors in a floating-point format", function () {
- // Float format is useful to WebGL.
- // RGB 0.0-1.1
- var i, j;
-
- // Used to verify one of R, G, B in loop below
- function verifyChannel(c) {
- expect(typeof c).toEqual("number");
- expect(c <= 1.0).toBeTruthy();
- expect(c >= 0.0).toBeTruthy();
- }
-
- for (i = 0; i < 30; i += 1) {
- // Verify that we got an array of numbers
- expect(Array.isArray(PlotPalette.getFloatColor(i)))
- .toBeTruthy();
- expect(PlotPalette.getFloatColor(i).length).toEqual(4);
-
- // Verify all three channels for type and range
- PlotPalette.getFloatColor(i).forEach(verifyChannel);
-
- // Verify uniqueness
- for (j = i + 1; j < 30; j += 1) {
- expect(PlotPalette.getFloatColor(i)).not.toEqual(
- PlotPalette.getFloatColor(j)
- );
- }
- }
- });
-
-
- it("has 30 unique colors in a string format", function () {
- // String format is useful in stylesheets
- // #RRGGBB in hex
- var i, j, c;
-
-
- for (i = 0; i < 30; i += 1) {
- c = PlotPalette.getStringColor(i);
-
- // Verify that we #-style color strings
- expect(typeof c).toEqual('string');
- expect(c.length).toEqual(7);
- expect(/^#[0-9a-fA-F]+$/.test(c)).toBeTruthy();
-
- // Verify uniqueness
- for (j = i + 1; j < 30; j += 1) {
- expect(PlotPalette.getStringColor(i)).not.toEqual(
- PlotPalette.getStringColor(j)
- );
- }
- }
- });
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotPanZoomStackGroupSpec.js b/platform/features/plot/test/elements/PlotPanZoomStackGroupSpec.js
deleted file mode 100644
index 6c4ae03776..0000000000
--- a/platform/features/plot/test/elements/PlotPanZoomStackGroupSpec.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/elements/PlotPanZoomStackGroup"],
- function (PlotPanZoomStackGroup) {
-
- var COUNT = 8;
-
- describe("A plot pan-zoom stack group", function () {
- var stacks,
- group;
-
- beforeEach(function () {
- group = new PlotPanZoomStackGroup(COUNT);
- stacks = [];
- while (stacks.length < COUNT) {
- stacks.push(group.getPanZoomStack(stacks.length));
- }
- });
-
- it("creates a number of separate stacks", function () {
- expect(group.getPanZoomStack(0)).toBeDefined();
- expect(group.getPanZoomStack(COUNT - 1)).toBeDefined();
- expect(group.getPanZoomStack(COUNT)).toBeUndefined();
- });
-
- it("synchronizes pan-zoom stack depth", function () {
- expect(group.getDepth()).toEqual(1);
- group.getPanZoomStack(1).pushPanZoom([10, 20], [30, 40]);
- stacks.forEach(function (stack) {
- expect(stack.getDepth()).toEqual(2);
- });
- });
-
- it("synchronizes domain but not range", function () {
- // Set up different initial states
- stacks.forEach(function (stack, i) {
- stack.pushPanZoom([i, i], [i, i]);
- });
-
- // Push a new pan-zoom state onto one of the stacks
- group.getPanZoomStack(1).pushPanZoom([99, 99], [42, 42]);
-
- // Should changed domain values for all stacks, but
- // only changed range values for stack 1
- stacks.forEach(function (stack, i) {
- expect(stack.getOrigin())
- .toEqual([99, i === 1 ? 99 : i]);
- expect(stack.getDimensions())
- .toEqual([42, i === 1 ? 42 : i]);
- });
- });
-
- it("synchronizes base pan-zoom", function () {
- group.setBasePanZoom([10, 9], [8, 7]);
- stacks.forEach(function (stack) {
- expect(stack.getOrigin()).toEqual([10, 9]);
- expect(stack.getDimensions()).toEqual([8, 7]);
- });
- });
-
- it("clears pan-zoom on request", function () {
- // Set up different initial states
- stacks.forEach(function (stack, i) {
- stack.pushPanZoom([i, i], [i, i]);
- });
-
- // Verify that we have a greater depth
- expect(group.getDepth() > 1).toBeTruthy();
-
- // Clear the pan-zoom state
- group.clearPanZoom();
-
- // Should be back down to our initial state
- expect(group.getDepth()).toEqual(1);
- stacks.forEach(function (stack) {
- expect(stack.getDepth()).toEqual(1);
- });
- });
-
- it("pops pan-zoom on request", function () {
- // Set up different initial states
- stacks.forEach(function (stack, i) {
- stack.pushPanZoom([i, i], [i, i]);
- });
-
- // Verify that we have a greater depth
- expect(group.getDepth()).toEqual(COUNT + 1);
-
- // Clear the pan-zoom state
- group.popPanZoom();
-
- // Should be back down to our initial state
- expect(group.getDepth()).toEqual(COUNT);
- stacks.forEach(function (stack) {
- expect(stack.getDepth()).toEqual(COUNT);
- });
- });
-
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotPanZoomStackSpec.js b/platform/features/plot/test/elements/PlotPanZoomStackSpec.js
deleted file mode 100644
index e4522faff2..0000000000
--- a/platform/features/plot/test/elements/PlotPanZoomStackSpec.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/elements/PlotPanZoomStack"],
- function (PlotPanZoomStack) {
-
- describe("A plot pan-zoom stack", function () {
- var panZoomStack,
- initialOrigin,
- initialDimensions,
- otherOrigins,
- otherDimensions;
-
- // Shorthand for verifying getOrigin, getDimensions, and getPanZoom,
- // which should always agree.
- function verifyPanZoom(origin, dimensions) {
- expect(panZoomStack.getOrigin()).toEqual(origin);
- expect(panZoomStack.getDimensions()).toEqual(dimensions);
- expect(panZoomStack.getPanZoom()).toEqual({
- origin: origin,
- dimensions: dimensions
- });
- }
-
- beforeEach(function () {
- initialOrigin = [4, 2];
- initialDimensions = [600, 400];
- otherOrigins = [[8, 6], [12, 9]];
- otherDimensions = [[400, 300], [200, 300]];
- panZoomStack =
- new PlotPanZoomStack(initialOrigin, initialDimensions);
- });
-
- it("starts off reporting its initial values", function () {
- verifyPanZoom(initialOrigin, initialDimensions);
- });
-
- it("allows origin/dimensions pairs to be pushed/popped", function () {
- panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
- verifyPanZoom(otherOrigins[0], otherDimensions[0]);
- panZoomStack.pushPanZoom(otherOrigins[1], otherDimensions[1]);
- verifyPanZoom(otherOrigins[1], otherDimensions[1]);
- panZoomStack.popPanZoom();
- verifyPanZoom(otherOrigins[0], otherDimensions[0]);
- panZoomStack.popPanZoom();
- verifyPanZoom(initialOrigin, initialDimensions);
- });
-
- it("reports current stack depth", function () {
- expect(panZoomStack.getDepth()).toEqual(1);
- panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
- expect(panZoomStack.getDepth()).toEqual(2);
- panZoomStack.pushPanZoom(otherOrigins[1], otherDimensions[1]);
- expect(panZoomStack.getDepth()).toEqual(3);
- });
-
- it("allows base pan zoom to be restored", function () {
- panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
- panZoomStack.pushPanZoom(otherOrigins[1], otherDimensions[1]);
- panZoomStack.clearPanZoom();
- verifyPanZoom(initialOrigin, initialDimensions);
- });
-
- it("allows base pan zoom to be changed", function () {
- panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
- panZoomStack.setBasePanZoom(otherOrigins[1], otherDimensions[1]);
- // Should not have changed current top-of-stack
- verifyPanZoom(otherOrigins[0], otherDimensions[0]);
-
- // Clear the stack - should be at our new base pan-zoom state
- panZoomStack.clearPanZoom();
- verifyPanZoom(otherOrigins[1], otherDimensions[1]);
- });
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotPositionSpec.js b/platform/features/plot/test/elements/PlotPositionSpec.js
deleted file mode 100644
index 47ef9dc939..0000000000
--- a/platform/features/plot/test/elements/PlotPositionSpec.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/elements/PlotPosition"],
- function (PlotPosition) {
-
- describe("A plot position", function () {
- var mockPanZoom,
- testOrigin = [10, 20],
- testDimensions = [800, 10];
-
- beforeEach(function () {
- mockPanZoom = jasmine.createSpyObj(
- "panZoomStack",
- ["getPanZoom"]
- );
- mockPanZoom.getPanZoom.andReturn({
- origin: testOrigin,
- dimensions: testDimensions
- });
- });
-
- it("transforms pixel coordinates to domain-range", function () {
- var position = new PlotPosition(42, 450, 100, 1000, mockPanZoom);
- // Domain: .42 * 800 + 10 = 346
- // Range: .55 * 10 + 20 = 25.5
- // Notably, y-axis is reversed between pixel space and range
- expect(position.getPosition()).toEqual([346, 25.5]);
- expect(position.getDomain()).toEqual(346);
- expect(position.getRange()).toEqual(25.5);
- });
-
- it("treats a position as undefined if no pan-zoom state is present", function () {
- var position;
-
- mockPanZoom.getPanZoom.andReturn({});
- position = new PlotPosition(1, 2, 100, 100, mockPanZoom);
- expect(position.getDomain()).toBeUndefined();
- expect(position.getRange()).toBeUndefined();
- expect(position.getPosition()).toEqual([]);
- });
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotPreparerSpec.js b/platform/features/plot/test/elements/PlotPreparerSpec.js
deleted file mode 100644
index 0e3a0c5bd0..0000000000
--- a/platform/features/plot/test/elements/PlotPreparerSpec.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/elements/PlotPreparer"],
- function (PlotPreparer) {
-
- var START = 123456;
-
- describe("A plot preparer", function () {
-
- function makeMockData(scale) {
- var mockData = jasmine.createSpyObj(
- "data" + scale,
- ["getPointCount", "getDomainValue", "getRangeValue"]
- );
- mockData.getPointCount.andReturn(1000);
- mockData.getDomainValue.andCallFake(function (i) {
- return START + i * 1000;
- });
- mockData.getRangeValue.andCallFake(function (i) {
- return Math.sin(i / 100) * scale;
- });
- return mockData;
- }
-
- it("fits to provided data sets", function () {
- var datas = [1, 2, 3].map(makeMockData),
- preparer = new PlotPreparer(datas);
-
- expect(preparer.getDomainOffset()).toEqual(START);
- expect(preparer.getOrigin()[0]).toBeCloseTo(START, 3);
- expect(preparer.getOrigin()[1]).toBeCloseTo(-3, 3);
- expect(preparer.getDimensions()[0]).toBeCloseTo(999000, 3);
- expect(preparer.getDimensions()[1]).toBeCloseTo(6, 3);
- });
-
- it("looks up values using a specified domain and range", function () {
- var datas = [makeMockData(1)],
- preparer = new PlotPreparer(datas, "testDomain", "testRange");
-
- expect(preparer).toBeDefined();
-
- expect(datas[0].getDomainValue).toHaveBeenCalledWith(
- jasmine.any(Number),
- "testDomain"
- );
-
- expect(datas[0].getRangeValue).toHaveBeenCalledWith(
- jasmine.any(Number),
- "testRange"
- );
- });
-
- it("provides a default range if data set is flat", function () {
- var datas = [makeMockData(0)],
- preparer = new PlotPreparer(datas);
-
- expect(preparer.getDimensions[1]).not.toEqual(0);
- });
-
- it("provides buffers", function () {
- var datas = [makeMockData(0)],
- preparer = new PlotPreparer(datas);
- expect(preparer.getBuffers()[0] instanceof Float32Array)
- .toBeTruthy();
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotSeriesWindowSpec.js b/platform/features/plot/test/elements/PlotSeriesWindowSpec.js
deleted file mode 100644
index 1bd339994f..0000000000
--- a/platform/features/plot/test/elements/PlotSeriesWindowSpec.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["../../src/elements/PlotSeriesWindow"],
- function (PlotSeriesWindow) {
-
- describe("A plot's window on a telemetry series", function () {
- var mockSeries,
- testSeries,
- window;
-
- beforeEach(function () {
- testSeries = [
- [0, 42],
- [10, 1],
- [20, 4],
- [30, 9],
- [40, 3]
- ];
-
- mockSeries = jasmine.createSpyObj(
- 'series',
- ['getPointCount', 'getDomainValue', 'getRangeValue']
- );
-
- mockSeries.getPointCount.andCallFake(function () {
- return testSeries.length;
- });
- mockSeries.getDomainValue.andCallFake(function (i) {
- return testSeries[i][0];
- });
- mockSeries.getRangeValue.andCallFake(function (i) {
- return testSeries[i][1];
- });
-
- window = new PlotSeriesWindow(
- mockSeries,
- "testDomain",
- "testRange",
- 1,
- testSeries.length
- );
- });
-
- it("provides a window upon a data series", function () {
- expect(window.getPointCount()).toEqual(4);
- expect(window.getDomainValue(0)).toEqual(10);
- expect(window.getRangeValue(0)).toEqual(1);
- });
-
- it("looks up using specific domain/range keys", function () {
- window.getDomainValue(0);
- window.getRangeValue(0);
- expect(mockSeries.getDomainValue)
- .toHaveBeenCalledWith(1, 'testDomain');
- expect(mockSeries.getRangeValue)
- .toHaveBeenCalledWith(1, 'testRange');
- });
-
- it("can be split into smaller windows", function () {
- var windows = window.split();
- expect(windows.length).toEqual(2);
- expect(windows[0].getPointCount()).toEqual(2);
- expect(windows[1].getPointCount()).toEqual(2);
- expect(windows[0].getDomainValue(0)).toEqual(10);
- expect(windows[1].getDomainValue(0)).toEqual(30);
- expect(windows[0].getRangeValue(0)).toEqual(1);
- expect(windows[1].getRangeValue(0)).toEqual(9);
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotTelemetryFormatterSpec.js b/platform/features/plot/test/elements/PlotTelemetryFormatterSpec.js
deleted file mode 100644
index 13c0729966..0000000000
--- a/platform/features/plot/test/elements/PlotTelemetryFormatterSpec.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["../../src/elements/PlotTelemetryFormatter"],
- function (PlotTelemetryFormatter) {
-
- describe("The PlotTelemetryFormatter", function () {
- var mockFormatter,
- formatter;
-
- beforeEach(function () {
- mockFormatter = jasmine.createSpyObj(
- 'telemetryFormatter',
- ['formatDomainValue', 'formatRangeValue']
- );
- formatter = new PlotTelemetryFormatter(mockFormatter);
- });
-
- describe("using domain & range format keys", function () {
- var rangeFormat = "someRangeFormat",
- domainFormat = "someDomainFormat";
-
- beforeEach(function () {
- formatter.setRangeFormat(rangeFormat);
- formatter.setDomainFormat(domainFormat);
- });
-
- it("includes format in formatDomainValue calls", function () {
- mockFormatter.formatDomainValue.andReturn("formatted!");
- expect(formatter.formatDomainValue(12321))
- .toEqual("formatted!");
- expect(mockFormatter.formatDomainValue)
- .toHaveBeenCalledWith(12321, domainFormat);
- });
-
- it("includes format in formatRangeValue calls for strings", function () {
- mockFormatter.formatRangeValue.andReturn("formatted!");
- expect(formatter.formatRangeValue('foo'))
- .toEqual("formatted!");
- expect(mockFormatter.formatRangeValue)
- .toHaveBeenCalledWith('foo', rangeFormat);
- });
-
- it("formats numeric values with three fixed digits", function () {
- expect(formatter.formatRangeValue(10)).toEqual("10.000");
- });
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotTickGeneratorSpec.js b/platform/features/plot/test/elements/PlotTickGeneratorSpec.js
deleted file mode 100644
index 7d94fca140..0000000000
--- a/platform/features/plot/test/elements/PlotTickGeneratorSpec.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/elements/PlotTickGenerator"],
- function (PlotTickGenerator) {
-
- describe("A plot tick generator", function () {
- var mockPanZoomStack,
- mockFormatter,
- generator;
-
- beforeEach(function () {
- mockPanZoomStack = jasmine.createSpyObj(
- "panZoomStack",
- ["getPanZoom"]
- );
- mockFormatter = jasmine.createSpyObj(
- "formatter",
- ["formatDomainValue", "formatRangeValue"]
- );
-
- mockPanZoomStack.getPanZoom.andReturn({
- origin: [0, 0],
- dimensions: [100, 100]
- });
-
- generator =
- new PlotTickGenerator(mockPanZoomStack, mockFormatter);
- });
-
- it("provides tick marks for range", function () {
- expect(generator.generateRangeTicks(11).length).toEqual(11);
-
- // Should have used range formatter
- expect(mockFormatter.formatRangeValue).toHaveBeenCalled();
- expect(mockFormatter.formatDomainValue).not.toHaveBeenCalled();
-
- });
-
- it("provides tick marks for domain", function () {
- expect(generator.generateDomainTicks(11).length).toEqual(11);
-
- // Should have used domain formatter
- expect(mockFormatter.formatRangeValue).not.toHaveBeenCalled();
- expect(mockFormatter.formatDomainValue).toHaveBeenCalled();
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/elements/PlotUpdaterSpec.js b/platform/features/plot/test/elements/PlotUpdaterSpec.js
deleted file mode 100644
index cd01a78ff2..0000000000
--- a/platform/features/plot/test/elements/PlotUpdaterSpec.js
+++ /dev/null
@@ -1,237 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/elements/PlotUpdater"],
- function (PlotUpdater) {
-
- describe("A plot updater", function () {
- var mockSubscription,
- testDomain,
- testRange,
- testDomainValues,
- testRangeValues,
- mockSeries,
- updater;
-
- function makeMockDomainObject(id) {
- var mockDomainObject = jasmine.createSpyObj(
- "object-" + id,
- ["getId", "getCapability", "getModel"]
- );
- mockDomainObject.getId.andReturn(id);
- return mockDomainObject;
- }
-
- beforeEach(function () {
- var ids = ['a', 'b', 'c'],
- mockObjects = ids.map(makeMockDomainObject);
-
- mockSubscription = jasmine.createSpyObj(
- "subscription",
- ["getDomainValue", "getRangeValue", "getTelemetryObjects"]
- );
- mockSeries = jasmine.createSpyObj(
- 'series',
- ['getPointCount', 'getDomainValue', 'getRangeValue']
- );
- testDomain = "testDomain";
- testRange = "testRange";
- testDomainValues = { a: 3, b: 7, c: 13 };
- testRangeValues = { a: 123, b: 456, c: 789 };
-
- mockSubscription.getTelemetryObjects.andReturn(mockObjects);
- mockSubscription.getDomainValue.andCallFake(function (mockObject) {
- return testDomainValues[mockObject.getId()];
- });
- mockSubscription.getRangeValue.andCallFake(function (mockObject) {
- return testRangeValues[mockObject.getId()];
- });
-
- updater = new PlotUpdater(
- mockSubscription,
- testDomain,
- testRange,
- 1350 // Smaller max size for easier testing
- );
- });
-
- it("provides one buffer per telemetry object", function () {
- expect(updater.getLineBuffers().length).toEqual(3);
- });
-
- it("changes buffer count if telemetry object counts change", function () {
- mockSubscription.getTelemetryObjects
- .andReturn([makeMockDomainObject('a')]);
- updater.update();
- expect(updater.getLineBuffers().length).toEqual(1);
- });
-
- it("can handle delayed telemetry object availability", function () {
- // The case can occur where getTelemetryObjects() returns an
- // empty array - specifically, while objects are still being
- // loaded. The updater needs to be able to cope with that
- // case.
- var tmp = mockSubscription.getTelemetryObjects();
- mockSubscription.getTelemetryObjects.andReturn([]);
-
- // Reinstantiate with the empty subscription
- updater = new PlotUpdater(
- mockSubscription,
- testDomain,
- testRange
- );
-
- // Should have 0 buffers for 0 objects
- expect(updater.getLineBuffers().length).toEqual(0);
-
- // Restore the three objects the test subscription would
- // normally have.
- mockSubscription.getTelemetryObjects.andReturn(tmp);
- updater.update();
-
- // Should have 3 buffers for 3 objects
- expect(updater.getLineBuffers().length).toEqual(3);
- });
-
- it("accepts historical telemetry updates", function () {
- var mockObject = mockSubscription.getTelemetryObjects()[0];
-
- mockSeries.getPointCount.andReturn(3);
- mockSeries.getDomainValue.andCallFake(function (i) {
- return 1000 + i * 1000;
- });
- mockSeries.getRangeValue.andReturn(10);
-
- // PlotLine & PlotLineBuffer are tested for most of the
- // details here, so just check for some expected side
- // effect; in this case, should see more points in the buffer
- expect(updater.getLineBuffers()[0].getLength()).toEqual(1);
- updater.addHistorical(mockObject, mockSeries);
- expect(updater.getLineBuffers()[0].getLength()).toEqual(4);
- });
-
- it("clears the domain offset if no objects are present", function () {
- mockSubscription.getTelemetryObjects.andReturn([]);
- updater.update();
- expect(updater.getDomainOffset()).toBeUndefined();
- });
-
- it("handles empty historical telemetry updates", function () {
- // General robustness check for when a series is empty
- var mockObject = mockSubscription.getTelemetryObjects()[0];
-
- mockSeries.getPointCount.andReturn(0);
- mockSeries.getDomainValue.andCallFake(function (i) {
- return 1000 + i * 1000;
- });
- mockSeries.getRangeValue.andReturn(10);
-
- // PlotLine & PlotLineBuffer are tested for most of the
- // details here, so just check for some expected side
- // effect; in this case, should see more points in the buffer
- expect(updater.getLineBuffers()[0].getLength()).toEqual(1);
- updater.addHistorical(mockObject, mockSeries);
- expect(updater.getLineBuffers()[0].getLength()).toEqual(1);
- });
-
- it("can initialize domain offset from historical telemetry", function () {
- var tmp = mockSubscription.getTelemetryObjects();
-
- mockSubscription.getTelemetryObjects.andReturn([]);
-
- // Reinstantiate with the empty subscription
- updater = new PlotUpdater(
- mockSubscription,
- testDomain,
- testRange
- );
-
- // Restore subscription, provide some historical data
- mockSubscription.getTelemetryObjects.andReturn(tmp);
- mockSeries.getPointCount.andReturn(3);
- mockSeries.getDomainValue.andCallFake(function (i) {
- return 1000 + i * 1000;
- });
- mockSeries.getRangeValue.andReturn(10);
-
- // PlotLine & PlotLineBuffer are tested for most of the
- // details here, so just check for some expected side
- // effect; in this case, should see more points in the buffer
- expect(updater.getDomainOffset()).toBeUndefined();
- updater.addHistorical(tmp[0], mockSeries);
- expect(updater.getDomainOffset()).toBeDefined();
- });
-
- it("provides some margin for the range", function () {
- var mockObject = mockSubscription.getTelemetryObjects()[0];
-
- mockSeries.getPointCount.andReturn(3);
- mockSeries.getDomainValue.andCallFake(function (i) {
- return 1000 + i * 1000;
- });
- mockSeries.getRangeValue.andCallFake(function (i) {
- return 10 + i; // 10, 20, 30
- });
- updater.addHistorical(mockObject, mockSeries);
- expect(updater.getOrigin()[1]).toBeLessThan(10);
- expect(updater.getDimensions()[1]).toBeGreaterThan(20);
- });
-
- describe("when no data is initially available", function () {
- beforeEach(function () {
- testDomainValues = {};
- testRangeValues = {};
- updater = new PlotUpdater(
- mockSubscription,
- testDomain,
- testRange,
- 1350 // Smaller max size for easier testing
- );
- });
-
- it("has no line data", function () {
- // Either no lines, or empty lines are fine
- expect(updater.getLineBuffers().map(function (lineBuffer) {
- return lineBuffer.getLength();
- }).reduce(function (a, b) {
- return a + b;
- }, 0)).toEqual(0);
- });
-
- it("determines initial domain bounds from first available data", function () {
- testDomainValues.a = 123;
- testRangeValues.a = 456;
- updater.update();
- expect(updater.getOrigin()[0]).toEqual(jasmine.any(Number));
- expect(updater.getOrigin()[1]).toEqual(jasmine.any(Number));
- expect(isNaN(updater.getOrigin()[0])).toBeFalsy();
- expect(isNaN(updater.getOrigin()[1])).toBeFalsy();
- });
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/modes/PlotModeOptionsSpec.js b/platform/features/plot/test/modes/PlotModeOptionsSpec.js
deleted file mode 100644
index ce792fd1b6..0000000000
--- a/platform/features/plot/test/modes/PlotModeOptionsSpec.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/modes/PlotModeOptions"],
- function (PlotModeOptions) {
-
- describe("Plot mode options", function () {
- var mockDomainObject,
- mockSubPlotFactory;
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability"]
- );
- mockSubPlotFactory = jasmine.createSpyObj(
- "subPlotFactory",
- ["createSubPlot"]
- );
- });
-
- it("offers only one option when one object is present", function () {
- expect(
- new PlotModeOptions([mockDomainObject], mockSubPlotFactory)
- .getModeOptions().length
- ).toEqual(1);
- });
-
- it("offers two options when multiple objects are present", function () {
- var objects = [
- mockDomainObject,
- mockDomainObject,
- mockDomainObject,
- mockDomainObject
- ];
- expect(
- new PlotModeOptions(objects, mockSubPlotFactory)
- .getModeOptions().length
- ).toEqual(2);
- });
-
- it("allows modes to be changed", function () {
- var plotModeOptions = new PlotModeOptions([
- mockDomainObject,
- mockDomainObject,
- mockDomainObject,
- mockDomainObject
- ], mockSubPlotFactory),
- initialHandler = plotModeOptions.getModeHandler();
-
- // Change the mode
- plotModeOptions.getModeOptions().forEach(function (option) {
- if (option !== plotModeOptions.getMode()) {
- plotModeOptions.setMode(option);
- }
- });
-
- // Mode should be different now
- expect(plotModeOptions.getModeHandler())
- .not.toBe(initialHandler);
- });
- });
- }
-);
diff --git a/platform/features/plot/test/modes/PlotOverlayModeSpec.js b/platform/features/plot/test/modes/PlotOverlayModeSpec.js
deleted file mode 100644
index cf1e0ce875..0000000000
--- a/platform/features/plot/test/modes/PlotOverlayModeSpec.js
+++ /dev/null
@@ -1,184 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/modes/PlotOverlayMode"],
- function (PlotOverlayMode) {
-
- describe("Overlaid plot mode", function () {
- var mockDomainObject,
- mockSubPlotFactory,
- mockPrepared,
- testBuffers,
- testDrawingObjects,
- mode;
-
- function createMockSubPlot() {
- var mockSubPlot = jasmine.createSpyObj(
- "subPlot",
- [
- "setDomainOffset",
- "hover",
- "startMarquee",
- "endMarquee",
- "getDrawingObject",
- "update"
- ]
- ),
- testDrawingObject = {};
-
- // Track drawing objects in order of creation
- testDrawingObjects.push(testDrawingObject);
- mockSubPlot.getDrawingObject.andReturn(testDrawingObject);
- return mockSubPlot;
- }
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability"]
- );
- mockSubPlotFactory = jasmine.createSpyObj(
- "subPlotFactory",
- ["createSubPlot"]
- );
- // Prepared telemetry data
- mockPrepared = jasmine.createSpyObj(
- "prepared",
- [
- "getDomainOffset",
- "getOrigin",
- "getDimensions",
- "getLineBuffers"
- ]
- );
-
- mockSubPlotFactory.createSubPlot.andCallFake(createMockSubPlot);
-
- // Act as if we have three buffers full of data
- testBuffers = ['a', 'b', 'c'].map(function (id) {
- var mockBuffer = jasmine.createSpyObj(
- 'buffer-' + id,
- ['getBuffer', 'getLength']
- );
- mockBuffer.getBuffer.andReturn([id]);
- mockBuffer.getLength.andReturn(3);
- return mockBuffer;
- });
- mockPrepared.getLineBuffers.andReturn(testBuffers);
- mockPrepared.getDomainOffset.andReturn(1234);
- mockPrepared.getOrigin.andReturn([10, 10]);
- mockPrepared.getDimensions.andReturn([500, 500]);
-
- // Clear out drawing objects
- testDrawingObjects = [];
-
- mode = new PlotOverlayMode([
- mockDomainObject,
- mockDomainObject,
- mockDomainObject
- ], mockSubPlotFactory);
- });
-
- it("creates one sub-plot for all domain objects", function () {
- expect(mode.getSubPlots().length).toEqual(1);
- });
-
- it("draws telemetry to subplots", function () {
- // Verify precondition
- mode.getSubPlots().forEach(function (subplot) {
- // Either empty list or undefined is fine;
- // just want to make sure there are no lines.
- expect(subplot.getDrawingObject().lines || [])
- .toEqual([]);
- });
-
- mode.plotTelemetry(mockPrepared);
-
- // Should have one sub-plot with three lines
- testDrawingObjects.forEach(function (testDrawingObject) {
- // Either empty list or undefined is fine;
- // just want to make sure there are no lines.
- expect(testDrawingObject.lines.length)
- .toEqual(3);
- // Make sure the right buffer was drawn to the
- // right subplot.
- testDrawingObject.lines.forEach(function (line, j) {
- expect(line.buffer).toEqual(testBuffers[j].getBuffer());
- });
- });
- });
-
- it("tracks zoomed state of subplots", function () {
- // Should start out unzoomed
- expect(mode.isZoomed()).toBeFalsy();
-
- // Trigger some zoom changes
- mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
- // Second argument to the factory was pan-zoom stack
- c.args[1].pushPanZoom([1, 2], [3, 4]);
- });
-
- // Should start out unzoomed
- expect(mode.isZoomed()).toBeTruthy();
- });
-
- it("supports unzooming", function () {
- // Trigger some zoom changes
- mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
- // Second argument to the factory was pan-zoom stack
- c.args[1].pushPanZoom([1, 2], [3, 4]);
- });
- // Verify that we are indeed zoomed now
- expect(mode.isZoomed()).toBeTruthy();
-
- // Unzoom
- mode.unzoom();
-
- // Should no longer be zoomed
- expect(mode.isZoomed()).toBeFalsy();
- });
-
- it("supports stepping back through zoom states", function () {
- // Trigger some zoom changes
- mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
- // Second argument to the factory was pan-zoom stack
- c.args[1].pushPanZoom([1, 2], [3, 4]);
- });
-
- // Step back the same number of zoom changes
- mockSubPlotFactory.createSubPlot.calls.forEach(function () {
- // Should still be zoomed at start of each iteration
- expect(mode.isZoomed()).toBeTruthy();
- // Step back one of the zoom changes.
- mode.stepBackPanZoom();
- });
-
- // Should no longer be zoomed
- expect(mode.isZoomed()).toBeFalsy();
- });
- });
- }
-);
diff --git a/platform/features/plot/test/modes/PlotStackModeSpec.js b/platform/features/plot/test/modes/PlotStackModeSpec.js
deleted file mode 100644
index cac4e18b82..0000000000
--- a/platform/features/plot/test/modes/PlotStackModeSpec.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * MergeModelsSpec. Created by vwoeltje on 11/6/14.
- */
-define(
- ["../../src/modes/PlotStackMode"],
- function (PlotStackMode) {
-
- describe("Stacked plot mode", function () {
- var mockDomainObject,
- mockSubPlotFactory,
- mockPrepared,
- testBuffers,
- testDrawingObjects,
- mode;
-
- function createMockSubPlot() {
- var mockSubPlot = jasmine.createSpyObj(
- "subPlot",
- [
- "setDomainOffset",
- "hover",
- "startMarquee",
- "endMarquee",
- "getDrawingObject",
- "update"
- ]
- ),
- testDrawingObject = {};
-
- // Track drawing objects in order of creation
- testDrawingObjects.push(testDrawingObject);
- mockSubPlot.getDrawingObject.andReturn(testDrawingObject);
- return mockSubPlot;
- }
-
- beforeEach(function () {
- mockDomainObject = jasmine.createSpyObj(
- "domainObject",
- ["getId", "getModel", "getCapability"]
- );
- mockSubPlotFactory = jasmine.createSpyObj(
- "subPlotFactory",
- ["createSubPlot"]
- );
- // Prepared telemetry data
- mockPrepared = jasmine.createSpyObj(
- "prepared",
- ["getDomainOffset", "getOrigin", "getDimensions", "getLineBuffers"]
- );
-
- mockSubPlotFactory.createSubPlot.andCallFake(createMockSubPlot);
-
- // Act as if we have three buffers full of data
- testBuffers = ['a', 'b', 'c'].map(function (id) {
- var mockBuffer = jasmine.createSpyObj(
- 'buffer-' + id,
- ['getBuffer', 'getLength']
- );
- mockBuffer.getBuffer.andReturn([id]);
- mockBuffer.getLength.andReturn(3);
- return mockBuffer;
- });
- mockPrepared.getLineBuffers.andReturn(testBuffers);
- mockPrepared.getDomainOffset.andReturn(1234);
- mockPrepared.getOrigin.andReturn([10, 10]);
- mockPrepared.getDimensions.andReturn([500, 500]);
-
- // Objects that will be drawn to in sub-plots
- testDrawingObjects = [];
-
- mode = new PlotStackMode([
- mockDomainObject,
- mockDomainObject,
- mockDomainObject
- ], mockSubPlotFactory);
- });
-
- it("creates one sub-plot per domain object", function () {
- expect(mode.getSubPlots().length).toEqual(3);
- });
-
- it("draws telemetry to subplots", function () {
- // Verify precondition
- mode.getSubPlots().forEach(function (subplot) {
- // Either empty list or undefined is fine;
- // just want to make sure there are no lines.
- expect(subplot.getDrawingObject().lines || [])
- .toEqual([]);
- });
-
- mode.plotTelemetry(mockPrepared);
-
- // Should all each have one line
- testDrawingObjects.forEach(function (testDrawingObject, i) {
- // Either empty list or undefined is fine;
- // just want to make sure there are no lines.
- expect(testDrawingObject.lines.length)
- .toEqual(1);
- // Make sure the right buffer was drawn to the
- // right subplot.
- expect(testDrawingObject.lines[0].buffer)
- .toEqual(testBuffers[i].getBuffer());
- });
- });
-
- it("tracks zoomed state of subplots", function () {
- // Should start out unzoomed
- expect(mode.isZoomed()).toBeFalsy();
-
- // Trigger some zoom changes
- mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
- // Second argument to the factory was pan-zoom stack
- c.args[1].pushPanZoom([1, 2], [3, 4]);
- });
-
- // Should start out unzoomed
- expect(mode.isZoomed()).toBeTruthy();
- });
-
- it("supports unzooming", function () {
- // Trigger some zoom changes
- mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
- // Second argument to the factory was pan-zoom stack
- c.args[1].pushPanZoom([1, 2], [3, 4]);
- });
- // Verify that we are indeed zoomed now
- expect(mode.isZoomed()).toBeTruthy();
-
- // Unzoom
- mode.unzoom();
-
- // Should no longer be zoomed
- expect(mode.isZoomed()).toBeFalsy();
- });
-
- it("supports stepping back through zoom states", function () {
- // Trigger some zoom changes
- mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
- // Second argument to the factory was pan-zoom stack
- c.args[1].pushPanZoom([1, 2], [3, 4]);
- });
-
- // Step back the same number of zoom changes
- mockSubPlotFactory.createSubPlot.calls.forEach(function () {
- // Should still be zoomed at start of each iteration
- expect(mode.isZoomed()).toBeTruthy();
- // Step back
- mode.stepBackPanZoom();
- });
-
- // Should no longer be zoomed
- expect(mode.isZoomed()).toBeFalsy();
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/policies/PlotViewPolicySpec.js b/platform/features/plot/test/policies/PlotViewPolicySpec.js
deleted file mode 100644
index fc1b2232aa..0000000000
--- a/platform/features/plot/test/policies/PlotViewPolicySpec.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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(
- ["../../src/policies/PlotViewPolicy"],
- function (PlotViewPolicy) {
-
- describe("Plot view policy", function () {
- var testView,
- mockDomainObject,
- testAdaptedObject,
- openmct,
- telemetryMetadata,
- policy;
-
- beforeEach(function () {
- testView = { key: "plot" };
- testAdaptedObject = { telemetry: {} };
- mockDomainObject = jasmine.createSpyObj(
- 'domainObject',
- ['useCapability', 'hasCapability', 'getCapability']
- );
- mockDomainObject.useCapability.andReturn(testAdaptedObject);
- openmct = {
- telemetry: jasmine.createSpyObj('telemetryAPI', [
- 'getMetadata'
- ])
- };
- telemetryMetadata = jasmine.createSpyObj('telemetryMetadata', [
- 'valuesForHints'
- ]);
- telemetryMetadata.valuesForHints.andReturn([]);
- openmct.telemetry.getMetadata.andReturn(telemetryMetadata);
- policy = new PlotViewPolicy(openmct);
- });
-
- it('fetches metadata from telem api', function () {
- policy.allow(testView, mockDomainObject);
- expect(mockDomainObject.useCapability)
- .toHaveBeenCalledWith('adapter');
- expect(openmct.telemetry.getMetadata)
- .toHaveBeenCalledWith(testAdaptedObject);
- expect(telemetryMetadata.valuesForHints)
- .toHaveBeenCalledWith(['range']);
- });
-
- it('returns false if no ranges exist', function () {
- telemetryMetadata.valuesForHints.andReturn([]);
- expect(policy.allow(testView, mockDomainObject)).toBe(false);
- });
-
- it('returns true if any ranges exist', function () {
- telemetryMetadata.valuesForHints.andReturn([{}]);
- expect(policy.allow(testView, mockDomainObject)).toBe(true);
- });
-
- it('returns false if all ranges are strings', function () {
- telemetryMetadata.valuesForHints.andReturn([{
- format: 'string'
- }, {
- format: 'string'
- }]);
- expect(policy.allow(testView, mockDomainObject)).toBe(false);
- });
-
- it('returns true if only some ranges are strings', function () {
- telemetryMetadata.valuesForHints.andReturn([{
- format: 'string'
- }, {}]);
- expect(policy.allow(testView, mockDomainObject)).toBe(true);
- });
-
- it('returns true for telemetry delegators', function () {
- delete testAdaptedObject.telemetry;
- mockDomainObject.hasCapability.andCallFake(function (c) {
- return c === 'delegation';
- });
- mockDomainObject.getCapability.andReturn(
- jasmine.createSpyObj('delegation', [
- 'doesDelegateCapability'
- ])
- );
- mockDomainObject.getCapability('delegation')
- .doesDelegateCapability.andCallFake(function (c) {
- return c === 'telemetry';
- });
- expect(policy.allow(testView, mockDomainObject)).toBe(true);
- expect(openmct.telemetry.getMetadata).not.toHaveBeenCalled();
- });
-
- it('returns true for non-telemetry non-delegators', function () {
- delete testAdaptedObject.telemetry;
- mockDomainObject.hasCapability.andReturn(false);
- expect(policy.allow(testView, mockDomainObject)).toBe(false);
- });
-
- it("allows other views", function () {
- testView.key = "somethingElse";
- expect(policy.allow(testView, mockDomainObject)).toBe(true);
- });
-
- });
- }
-);
diff --git a/platform/features/plot/test/services/ExportImageServiceSpec.js b/platform/features/plot/test/services/ExportImageServiceSpec.js
deleted file mode 100644
index a898a9ad6e..0000000000
--- a/platform/features/plot/test/services/ExportImageServiceSpec.js
+++ /dev/null
@@ -1,146 +0,0 @@
-/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, 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.
- *****************************************************************************/
-
-/**
- * ExportImageServiceSpec. Created by hudsonfoo on 09/03/16.
- */
-define(
- ["../../src/services/ExportImageService"],
- function (ExportImageService) {
- var mockQ,
- mockDeferred,
- mockPromise,
- mockTimeout,
- mockLog,
- mockHtml2Canvas,
- mockCanvas,
- mockSaveAs,
- mockFileReader,
- mockExportTimeoutConstant,
- testElement,
- exportImageService,
- mockChangeBackgroundColor;
-
- describe("ExportImageService", function () {
- beforeEach(function () {
- mockDeferred = jasmine.createSpyObj(
- "deferred",
- ["reject", "resolve"]
- );
- mockPromise = jasmine.createSpyObj(
- "promise",
- ["then", "finally"]
- );
- mockPromise.then = function (callback) {
- callback();
- };
- mockQ = {
- "defer": function () {
- return {
- "resolve": mockDeferred.resolve,
- "reject": mockDeferred.reject,
- "promise": mockPromise
- };
- }
- };
- mockTimeout = function (fn, time) {
- return {
- "cancel": function () {}
- };
- };
- mockLog = jasmine.createSpyObj(
- "$log",
- ["warn"]
- );
- mockHtml2Canvas = jasmine.createSpy("html2canvas").andCallFake(function (element, opts) {
- opts.onrendered(mockCanvas);
- });
- mockCanvas = jasmine.createSpyObj(
- "canvas",
- ["toBlob"]
- );
- mockSaveAs = jasmine.createSpy("saveAs");
- mockFileReader = jasmine.createSpyObj(
- "FileReader",
- ["readAsDataURL", "onloadend"]
- );
- mockExportTimeoutConstant = 0;
- testElement = {style: {backgroundColor: 'black'}};
-
- mockChangeBackgroundColor = jasmine.createSpy('changeBackgroundColor');
-
- exportImageService = new ExportImageService(
- mockQ,
- mockTimeout,
- mockLog,
- mockExportTimeoutConstant,
- mockHtml2Canvas,
- mockSaveAs,
- mockFileReader,
- mockChangeBackgroundColor
- );
- });
-
- it("runs html2canvas and tries to save a png", function () {
- exportImageService.exportPNG(testElement, "plot.png");
-
- expect(mockHtml2Canvas).toHaveBeenCalledWith(testElement, { onrendered: jasmine.any(Function) });
- expect(mockCanvas.toBlob).toHaveBeenCalledWith(mockDeferred.resolve, "image/png");
- expect(mockDeferred.reject).not.toHaveBeenCalled();
- expect(mockSaveAs).toHaveBeenCalled();
- expect(mockPromise.finally).toHaveBeenCalled();
- });
-
- it("runs html2canvas and tries to save a jpg", function () {
- exportImageService.exportJPG(testElement, "plot.png");
-
- expect(mockHtml2Canvas).toHaveBeenCalledWith(testElement, { onrendered: jasmine.any(Function) });
- expect(mockCanvas.toBlob).toHaveBeenCalledWith(mockDeferred.resolve, "image/jpeg");
- expect(mockDeferred.reject).not.toHaveBeenCalled();
- expect(mockSaveAs).toHaveBeenCalled();
- expect(mockPromise.finally).toHaveBeenCalled();
- });
-
- it("changes background color to white and returns color back to original after snapshot, for better visibility of plot lines on print", function () {
- exportImageService.exportPNG(testElement, "plot.png", 'white');
-
- expect(mockChangeBackgroundColor).toHaveBeenCalledWith(testElement, 'white');
- expect(mockChangeBackgroundColor).toHaveBeenCalledWith(testElement, 'black');
-
- exportImageService.exportJPG(testElement, "plot.jpg", 'white');
-
- expect(mockChangeBackgroundColor).toHaveBeenCalledWith(testElement, 'white');
- expect(mockChangeBackgroundColor).toHaveBeenCalledWith(testElement, 'black');
- });
-
- it("does not change background color when color is not specified in parameters", function () {
- exportImageService.exportPNG(testElement, "plot.png");
-
- expect(mockChangeBackgroundColor).not.toHaveBeenCalled();
-
- exportImageService.exportJPG(testElement, "plot.jpg");
-
- expect(mockChangeBackgroundColor).not.toHaveBeenCalled();
- });
- });
- }
-);
diff --git a/platform/telemetry/src/TelemetryCapability.js b/platform/telemetry/src/TelemetryCapability.js
index 38258cdb71..097925a2aa 100644
--- a/platform/telemetry/src/TelemetryCapability.js
+++ b/platform/telemetry/src/TelemetryCapability.js
@@ -205,6 +205,9 @@ define(
},
getPointCount: function () {
return telemetry.length;
+ },
+ getData: function () {
+ return telemetry;
}
};
}
diff --git a/src/api/telemetry/LegacyTelemetryProvider.js b/src/api/telemetry/LegacyTelemetryProvider.js
index c0f40eb85a..05f1d66ba0 100644
--- a/src/api/telemetry/LegacyTelemetryProvider.js
+++ b/src/api/telemetry/LegacyTelemetryProvider.js
@@ -148,7 +148,9 @@ define([
var limitEvaluator = oldObject.getCapability("limit");
if (!limitEvaluator) {
- return;
+ return {
+ evaluate: function () {}
+ };
}
return {
diff --git a/src/api/telemetry/TelemetryMetadataManager.js b/src/api/telemetry/TelemetryMetadataManager.js
index e8a41806e7..52b152b741 100644
--- a/src/api/telemetry/TelemetryMetadataManager.js
+++ b/src/api/telemetry/TelemetryMetadataManager.js
@@ -100,6 +100,18 @@ define([
delete valueMetadata.hints.y;
}
+ if (valueMetadata.format === 'enum') {
+ if (!valueMetadata.values) {
+ valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
+ }
+ if (!valueMetadata.hasOwnProperty('max')) {
+ valueMetadata.max = _.max(valueMetadata.values) + 1;
+ }
+ if (!valueMetadata.hasOwnProperty('min')) {
+ valueMetadata.min = _.min(valueMetadata.values) - 1;
+ }
+ }
+
if (!valueMetadata.hints.hasOwnProperty('priority')) {
valueMetadata.hints.priority = index;
}
diff --git a/src/api/telemetry/TelemetryValueFormatter.js b/src/api/telemetry/TelemetryValueFormatter.js
index c4db2560f3..2153b479c8 100644
--- a/src/api/telemetry/TelemetryValueFormatter.js
+++ b/src/api/telemetry/TelemetryValueFormatter.js
@@ -49,7 +49,7 @@ define([
this.formatter = numberFormatter;
}
- if (valueMetadata.type === 'enum') {
+ if (valueMetadata.format === 'enum') {
this.formatter = {};
this.enumerations = valueMetadata.enumerations.reduce(function (vm, e) {
vm.byValue[e.value] = e.string;
@@ -57,11 +57,16 @@ define([
return vm;
}, {byValue: {}, byString: {}});
this.formatter.format = function (value) {
- return this.enumerations.byValue[value];
+ if (typeof value === "number") {
+ return this.enumerations.byValue[value] || value;
+ }
+ return value;
}.bind(this);
this.formatter.parse = function (string) {
- if (typeof string === "string" && this.enumerations.hasOwnProperty(string)) {
- return this.enumerations.byString[string];
+ if (typeof string === "string") {
+ if (this.enumerations.byString.hasOwnProperty(string)) {
+ return this.enumerations.byString[string];
+ }
}
return Number(string);
}.bind(this);
diff --git a/src/defaultRegistry.js b/src/defaultRegistry.js
index 239571e3b3..d138998fa9 100644
--- a/src/defaultRegistry.js
+++ b/src/defaultRegistry.js
@@ -73,7 +73,6 @@ define([
'../platform/features/my-items/bundle',
'../platform/features/pages/bundle',
'../platform/features/hyperlink/bundle',
- '../platform/features/plot/bundle',
'../platform/features/static-markup/bundle',
'../platform/features/table/bundle',
'../platform/features/timeline/bundle',
@@ -120,7 +119,6 @@ define([
'platform/features/listview',
'platform/features/pages',
'platform/features/hyperlink',
- 'platform/features/plot',
'platform/features/timeline',
'platform/features/table',
'platform/forms',
diff --git a/src/plugins/plot/plugin.js b/src/plugins/plot/plugin.js
new file mode 100644
index 0000000000..d217e2f47d
--- /dev/null
+++ b/src/plugins/plot/plugin.js
@@ -0,0 +1,250 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+
+/*global define*/
+
+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/HideElementPoolDirective",
+ "./src/services/ExportImageService",
+ './src/PlotViewPolicy',
+ "text!./res/templates/plot-options.html",
+ "text!./res/templates/plot-options-browse.html",
+ "text!./res/templates/plot-options-edit.html",
+ "text!./res/templates/stacked-plot.html",
+ "text!./res/templates/plot.html"
+], function (
+ MCTChartDirective,
+ MCTPlotDirective,
+ MCTTicksDirective,
+ MCTOverlayPlot,
+ PlotController,
+ StackedPlotController,
+ PlotInspector,
+ PlotOptionsController,
+ HideElementPool,
+ ExportImageService,
+ PlotViewPolicy,
+ plotOptionsTemplate,
+ plotOptionsBrowseTemplate,
+ plotOptionsEditTemplate,
+ StackedPlotTemplate,
+ PlotTemplate
+) {
+
+ var installed = false;
+
+ function PlotPlugin() {
+ return function install(openmct) {
+ if (installed) {
+ return;
+ }
+ installed = true;
+
+ 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"
+ ]
+ }
+ ],
+ "services": [
+ {
+ "key": "exportImageService",
+ "implementation": ExportImageService,
+ "depends": [
+ "$q",
+ "$timeout",
+ "$log"
+ ]
+ }
+ ],
+ "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": []
+ },
+ "properties": [],
+ "priority": 890
+ }
+ ],
+ "representations": [
+ {
+ "key": "plot-options",
+ "template": plotOptionsTemplate
+ },
+ {
+ "key": "plot-options-browse",
+ "template": plotOptionsBrowseTemplate
+ },
+ {
+ "key": "plot-options-edit",
+ "template": plotOptionsEditTemplate
+ }
+ ]
+ }
+ });
+
+ openmct.legacyRegistry.enable("openmct/plot");
+ };
+ }
+
+ return PlotPlugin;
+});
diff --git a/src/plugins/plot/res/templates/mct-plot.html b/src/plugins/plot/res/templates/mct-plot.html
new file mode 100644
index 0000000000..8f764ace3d
--- /dev/null
+++ b/src/plugins/plot/res/templates/mct-plot.html
@@ -0,0 +1,210 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ series.get('name') }}
+
+
+ {{ 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']);
+ }}
+
+
+
+
+
+
+
+
+
+ | Name |
+
+ Timestamp
+ |
+
+ Value
+ |
+
+ Min
+ |
+
+ Max
+ |
+
+
+
+ |
+
+
+ {{ series.get('name') }}
+ |
+
+
+
+ {{ series.closest && series.formatX(series.closest) }}
+
+ |
+
+
+ {{ series.formatY(series.closest) }}
+
+ |
+
+
+ {{ series.formatY(series.get('stats').minPoint) }}
+
+ |
+
+
+ {{ series.formatY(series.get('stats').maxPoint) }}
+
+ |
+
+
+
+
+
+
+
+
+
+ {{ yAxis.get('label') }}
+
+
+
+
+ {{:: tick.text}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{:: tick.text | reverse}}
+
+
+
+
+ {{ xAxis.get('label') }}
+
+
+
+
+
+
+
diff --git a/src/plugins/plot/res/templates/plot-options-browse.html b/src/plugins/plot/res/templates/plot-options-browse.html
new file mode 100644
index 0000000000..74699cdba2
--- /dev/null
+++ b/src/plugins/plot/res/templates/plot-options-browse.html
@@ -0,0 +1,130 @@
+
+
+
-
+
+
+
+ -
+
+
+
Label
+
{{ config.yAxis.get('label') }}
+
+
+
Autoscale
+
+ {{ config.yAxis.get('autoscale') ? "On" : "Off" }}
+ {{ config.yAxis.get('autoscale') ? (config.yAxis.get('autoscalePadding') * 100) + "%" : ""}}
+
+
+
+
Min
+
{{ config.yAxis.get('range').min }}
+
+
+
Max
+
{{ config.yAxis.get('range').max }}
+
+
+ -
+
+
+
Position
+
{{ config.legend.get('position') }}
+
+
+
Expand by Default
+
{{ config.legend.get('expandByDefault') ? "Yes" : "No" }}
+
+
+
Show when collapsed:
+
{{
+ config.legend.get('valueToShowWhenCollapsed').replace('nearest', '')
+ }}
+
+
+
Show when expanded:
+
+ Timestamp
+ Value
+ Min
+ Max
+
+
+
+
+
diff --git a/src/plugins/plot/res/templates/plot-options-edit.html b/src/plugins/plot/res/templates/plot-options-edit.html
new file mode 100644
index 0000000000..4df65887c1
--- /dev/null
+++ b/src/plugins/plot/res/templates/plot-options-edit.html
@@ -0,0 +1,229 @@
+
+
diff --git a/src/plugins/plot/res/templates/plot-options.html b/src/plugins/plot/res/templates/plot-options.html
new file mode 100644
index 0000000000..c34594db4d
--- /dev/null
+++ b/src/plugins/plot/res/templates/plot-options.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/plugins/plot/res/templates/plot.html b/src/plugins/plot/res/templates/plot.html
new file mode 100644
index 0000000000..3b585a19e5
--- /dev/null
+++ b/src/plugins/plot/res/templates/plot.html
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/plugins/plot/res/templates/stacked-plot.html b/src/plugins/plot/res/templates/stacked-plot.html
new file mode 100644
index 0000000000..237ed9590c
--- /dev/null
+++ b/src/plugins/plot/res/templates/stacked-plot.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
diff --git a/platform/features/plot/src/policies/PlotViewPolicy.js b/src/plugins/plot/src/PlotViewPolicy.js
similarity index 95%
rename from platform/features/plot/src/policies/PlotViewPolicy.js
rename to src/plugins/plot/src/PlotViewPolicy.js
index 0d6e082386..ca23502cbe 100644
--- a/platform/features/plot/src/policies/PlotViewPolicy.js
+++ b/src/plugins/plot/src/PlotViewPolicy.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, United States Government
+ * Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -54,7 +54,7 @@ define(
};
PlotViewPolicy.prototype.allow = function (view, domainObject) {
- if (view.key === 'plot') {
+ if (view.key === 'plot-single') {
return this.hasNumericTelemetry(domainObject);
}
diff --git a/src/plugins/plot/src/chart/MCTChartAlarmPointSet.js b/src/plugins/plot/src/chart/MCTChartAlarmPointSet.js
new file mode 100644
index 0000000000..2282cb4130
--- /dev/null
+++ b/src/plugins/plot/src/chart/MCTChartAlarmPointSet.js
@@ -0,0 +1,77 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define*/
+
+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;
+
+});
diff --git a/src/plugins/plot/src/chart/MCTChartController.js b/src/plugins/plot/src/chart/MCTChartController.js
new file mode 100644
index 0000000000..417e60c8b5
--- /dev/null
+++ b/src/plugins/plot/src/chart/MCTChartController.js
@@ -0,0 +1,400 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define,requestAnimationFrame,Float32Array*/
+
+/**
+ * Module defining MCTChart. Created by vwoeltje on 11/12/14.
+ */
+define([
+ './MCTChartLineLinear',
+ './MCTChartLineStepAfter',
+ './MCTChartPointSet',
+ './MCTChartAlarmPointSet',
+ '../draw/DrawLoader',
+ '../lib/eventHelpers',
+ 'lodash'
+],
+function (
+ MCTChartLineLinear,
+ MCTChartLineStepAfter,
+ MCTChartPointSet,
+ MCTChartAlarmPointSet,
+ DrawLoader,
+ eventHelpers,
+ _
+) {
+
+ var MARKER_SIZE = 6.0,
+ 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.$scope = $scope;
+ this.isDestroyed = false;
+ this.lines = [];
+ this.pointSets = [];
+ this.alarmSets = [];
+ this.offset = {};
+ this.config = $scope.config;
+ this.listenTo(this.$scope, '$destoy', 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.xAxis, 'change:key', this.clearOffset, this);
+ this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
+ this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
+ this.$scope.$watch('highlights', this.scheduleDraw);
+ this.$scope.$watch('rectangles', this.scheduleDraw);
+ this.config.series.forEach(this.onSeriesAdd, this);
+ }
+
+ eventHelpers.extend(MCTChartController.prototype);
+
+ MCTChartController.$inject = ['$scope'];
+
+ MCTChartController.prototype.onSeriesAdd = function (series) {
+ 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;
+ }
+ var elements = this.seriesElements.get(series);
+ elements.lines.forEach(function (line) {
+ this.lines.splice(this.lines.indexOf(line), 1);
+ line.destroy();
+ }, this);
+ elements.lines = [];
+
+ var 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;
+ }
+ var 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;
+ }
+ var elements = this.seriesElements.get(series);
+ elements.pointSets.forEach(function (pointSet) {
+ this.pointSets.splice(this.pointSets.indexOf(pointSet), 1);
+ pointSet.destroy();
+ }, this);
+ elements.pointSets = [];
+
+ var 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();
+ _.invoke(this.lines, '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;
+ }
+
+ var 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);
+ return !!this.drawAPI;
+ };
+
+ MCTChartController.prototype.removeChartElement = function (series) {
+ var 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);
+ 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) {
+ var elements = {
+ lines: [],
+ pointSets: []
+ };
+
+ var line = this.lineForSeries(series);
+ if (line) {
+ elements.lines.push(line);
+ this.lines.push(line);
+ }
+
+ var 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 () {
+ var xRange = this.config.xAxis.get('displayRange'),
+ yRange = this.config.yAxis.get('displayRange');
+
+ if (!xRange || !yRange) {
+ return;
+ }
+
+ var dimensions = [
+ xRange.max - xRange.min,
+ yRange.max - yRange.min
+ ],
+ 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')
+ );
+ };
+
+ 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) {
+ var points = new Float32Array([
+ this.offset.xVal(highlight.point, highlight.series),
+ this.offset.yVal(highlight.point, highlight.series)
+ ]),
+ color = highlight.series.get('color').asRGBAArray(),
+ pointCount = 1;
+
+ this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE);
+ };
+
+ 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;
+});
diff --git a/src/plugins/plot/src/chart/MCTChartDirective.js b/src/plugins/plot/src/chart/MCTChartDirective.js
new file mode 100644
index 0000000000..5662465878
--- /dev/null
+++ b/src/plugins/plot/src/chart/MCTChartDirective.js
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define*/
+
+/**
+ * Module defining MCTChart. Created by vwoeltje on 11/12/14.
+ */
+define([
+ './MCTChartController'
+], function (
+ MCTChartController
+) {
+
+ var TEMPLATE = "";
+ TEMPLATE += TEMPLATE;
+
+ /**
+ * MCTChart draws charts utilizing a drawAPI.
+ *
+ * @constructor
+ */
+ function MCTChart() {
+ return {
+ restrict: "E",
+ template: TEMPLATE,
+ link: function ($scope, $element, attrs, ctrl) {
+ var mainCanvas = $element.find("canvas")[1];
+ var 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;
+});
diff --git a/src/plugins/plot/src/chart/MCTChartLineLinear.js b/src/plugins/plot/src/chart/MCTChartLineLinear.js
new file mode 100644
index 0000000000..1db828adfd
--- /dev/null
+++ b/src/plugins/plot/src/chart/MCTChartLineLinear.js
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define*/
+
+define([
+ './MCTChartSeriesElement'
+], function (
+ MCTChartSeriesElement
+) {
+
+ var MCTChartLineLinear = MCTChartSeriesElement.extend({
+ addPoint: function (point, start, count) {
+ this.buffer[start] = point.x;
+ this.buffer[start + 1] = point.y;
+ }
+ });
+
+ return MCTChartLineLinear;
+
+});
+
diff --git a/src/plugins/plot/src/chart/MCTChartLineStepAfter.js b/src/plugins/plot/src/chart/MCTChartLineStepAfter.js
new file mode 100644
index 0000000000..447c68dffe
--- /dev/null
+++ b/src/plugins/plot/src/chart/MCTChartLineStepAfter.js
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define*/
+
+define([
+ './MCTChartSeriesElement'
+], function (
+ MCTChartSeriesElement
+) {
+
+ var 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;
+
+});
+
diff --git a/src/plugins/plot/src/chart/MCTChartPointSet.js b/src/plugins/plot/src/chart/MCTChartPointSet.js
new file mode 100644
index 0000000000..92913fa92f
--- /dev/null
+++ b/src/plugins/plot/src/chart/MCTChartPointSet.js
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define*/
+
+define([
+ './MCTChartSeriesElement'
+], function (
+ MCTChartSeriesElement
+) {
+
+ var MCTChartPointSet = MCTChartSeriesElement.extend({
+ addPoint: function (point, start, count) {
+ this.buffer[start] = point.x;
+ this.buffer[start + 1] = point.y;
+ }
+ });
+
+ return MCTChartPointSet;
+
+});
+
diff --git a/src/plugins/plot/src/chart/MCTChartSeriesElement.js b/src/plugins/plot/src/chart/MCTChartSeriesElement.js
new file mode 100644
index 0000000000..8940d98497
--- /dev/null
+++ b/src/plugins/plot/src/chart/MCTChartSeriesElement.js
@@ -0,0 +1,162 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define,Float32Array*/
+
+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) {
+ var target = index,
+ start = index + count,
+ end = this.count * 2;
+ this.buffer.copyWithin(target, start, end);
+ for (var 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) {
+ var vertexCount = this.vertexCountForPointAtIndex(index);
+ var 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) {
+ var pointsRequired = this.vertexCountForPointAtIndex(index);
+ var 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;
+ }
+ var target = insertionPoint + pointsRequired,
+ 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) {
+ var remainingPoints = this.buffer.length - this.count * 2;
+ var 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;
+});
diff --git a/src/plugins/plot/src/configuration/Collection.js b/src/plugins/plot/src/configuration/Collection.js
new file mode 100644
index 0000000000..d12cb45cb5
--- /dev/null
+++ b/src/plugins/plot/src/configuration/Collection.js
@@ -0,0 +1,133 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define*/
+
+define([
+ 'lodash',
+ '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);
+ }
+
+ _.extend(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);
+ var 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 _.findIndex(
+ this.models,
+ function (m) {
+ return m === model;
+ }
+ );
+ };
+
+ Collection.prototype.remove = function (model) {
+ var index = this.indexOf(model);
+ if (index === -1) {
+ throw new Error('model not found in collection.');
+ }
+ this.models.splice(index, 1);
+ this.emit('remove', model, index);
+ };
+
+ Collection.prototype.destroy = function (model) {
+ this.forEach(function (m) {
+ m.destroy();
+ });
+ this.stopListening();
+ };
+
+ return Collection;
+});
diff --git a/src/plugins/plot/src/configuration/LegendModel.js b/src/plugins/plot/src/configuration/LegendModel.js
new file mode 100644
index 0000000000..fd13073292
--- /dev/null
+++ b/src/plugins/plot/src/configuration/LegendModel.js
@@ -0,0 +1,61 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ */
+ var 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 () {
+ var 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,
+ valueToShowWhenCollapsed: 'nearestValue',
+ showTimestampWhenExpanded: true,
+ showValueWhenExpanded: true,
+ showMaximumWhenExpanded: true,
+ showMinimumWhenExpanded: true
+ };
+ }
+ });
+
+ return LegendModel;
+});
diff --git a/src/plugins/plot/src/configuration/Model.js b/src/plugins/plot/src/configuration/Model.js
new file mode 100644
index 0000000000..683b040a09
--- /dev/null
+++ b/src/plugins/plot/src/configuration/Model.js
@@ -0,0 +1,102 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define*/
+
+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;
+ var defaults = this.defaults(options);
+ if (!this.model) {
+ this.model = options.model = defaults;
+ } else {
+ _.defaultsDeep(this.model, defaults);
+ }
+ this.initialize(options);
+ }
+
+ _.extend(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) {
+ var 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) {
+ var oldValue = this.model[attribute];
+ delete this.model[attribute];
+ this.emit('change', attribute, undefined, oldValue, this);
+ this.emit('change:' + attribute, undefined, oldValue, this);
+ };
+
+ return Model;
+});
diff --git a/src/plugins/plot/src/configuration/PlotConfigurationModel.js b/src/plugins/plot/src/configuration/PlotConfigurationModel.js
new file mode 100644
index 0000000000..e00614ca5b
--- /dev/null
+++ b/src/plugins/plot/src/configuration/PlotConfigurationModel.js
@@ -0,0 +1,134 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define*/
+
+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.
+ *
+ */
+ var 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
+ });
+
+ 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) {
+ var 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];
+ },
+ /**
+ * 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();
+ 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;
+});
diff --git a/src/plugins/plot/src/configuration/PlotSeries.js b/src/plugins/plot/src/configuration/PlotSeries.js
new file mode 100644
index 0000000000..145bbc564e
--- /dev/null
+++ b/src/plugins/plot/src/configuration/PlotSeries.js
@@ -0,0 +1,357 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/*global define*/
+
+define([
+ 'lodash',
+ '../configuration/Model',
+ '../lib/extend',
+ 'EventEmitter'
+], function (
+ _,
+ Model,
+ extend,
+ EventEmitter
+) {
+
+ /**
+ * 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:` 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.
+ * `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.
+ */
+ var 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);
+
+ Model.apply(this, arguments);
+ this.onXKeyChange(this.get('xKey'));
+ this.onYKeyChange(this.get('yKey'));
+ },
+
+ /**
+ * Set defaults for telemetry series.
+ */
+ defaults: function (options) {
+ var range = this.metadata.valuesForHints(['range'])[0];
+ return {
+ name: options.domainObject.name,
+ xKey: options.collection.plot.xAxis.get('key'),
+ yKey: range.key,
+ markers: true,
+ 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.limitEvaluator = this.openmct.telemetry.limitEvaluator(options.domainObject);
+ this.on('destroy', this.onDestroy, 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) {
+ options = _.extend({}, {size: 1000, strategy: 'minmax'}, options || {});
+ if (!this.unsubscribe) {
+ this.unsubscribe = this.openmct
+ .telemetry
+ .subscribe(
+ this.domainObject,
+ this.add.bind(this)
+ );
+ }
+
+ return this.openmct
+ .telemetry
+ .request(this.domainObject, options)
+ .then(function (points) {
+ var newPoints = _(this.data)
+ .concat(points)
+ .sortBy(this.getXVal)
+ .uniq(true, this.getXVal)
+ .value();
+ this.reset(newPoints);
+ }.bind(this));
+ },
+ /**
+ * Update x formatter on x change.
+ */
+ onXKeyChange: function (xKey) {
+ var 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;
+ }
+ var valueMetadata = this.metadata.value(newKey);
+ var persistedConfig = this.get('persistedConfiguration');
+ if (!persistedConfig || !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);
+ var 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) {
+ var insertIndex = this.sortedIndex(xValue),
+ lowPoint = this.data[insertIndex - 1],
+ highPoint = this.data[insertIndex],
+ indexVal = this.getXVal(xValue),
+ lowDistance = lowPoint ?
+ indexVal - this.getXVal(lowPoint) :
+ Number.POSITIVE_INFINITY,
+ highDistance = highPoint ?
+ this.getXVal(highPoint) - indexVal :
+ Number.POSITIVE_INFINITY,
+ 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 _.sortedIndex(this.data, point, this.getXVal);
+ },
+ /**
+ * Update min/max stats for the series.
+ * @private
+ */
+ updateStats: function (point) {
+ var value = this.getYVal(point);
+ var stats = this.get('stats');
+ var 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) {
+ var insertIndex = this.data.length;
+ 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);
+ },
+ /**
+ * Remove a point from the data array and notify listeners.
+ * @private
+ */
+ remove: function (point) {
+ var 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) {
+ var startIndex = this.sortedIndex(range.min);
+ var endIndex = this.sortedIndex(range.max) + 1;
+ var 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 {
+ var newData = this.data.slice(startIndex, endIndex);
+ this.reset(newData);
+ }
+ }
+
+ }
+ });
+
+ return PlotSeries;
+
+});
diff --git a/src/plugins/plot/src/configuration/SeriesCollection.js b/src/plugins/plot/src/configuration/SeriesCollection.js
new file mode 100644
index 0000000000..845663f05f
--- /dev/null
+++ b/src/plugins/plot/src/configuration/SeriesCollection.js
@@ -0,0 +1,158 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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,
+ _
+) {
+
+ var 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);
+
+ var domainObject = this.plot.get('domainObject');
+ if (domainObject.telemetry) {
+ this.addTelemetryObject(domainObject);
+ } else {
+ this.watchTelemetryContainer(domainObject);
+ }
+ },
+ trackPersistedConfig: function (domainObject) {
+ domainObject.configuration.series.forEach(function (seriesConfig) {
+ var series = this.byIdentifier(seriesConfig.identifier);
+ if (series) {
+ series.set('persistedConfiguration', seriesConfig);
+ }
+ }, this);
+ },
+ watchTelemetryContainer: function (domainObject) {
+ var 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) {
+ var seriesConfig = {
+ identifier: domainObject.identifier
+ };
+
+ var plotObject = this.plot.get('domainObject');
+ if (plotObject.type === 'telemetry.plot.overlay') {
+ var index = this.size();
+ if (!plotObject.configuration.series[index]) {
+ this.openmct.objects.mutate(
+ plotObject,
+ 'configuration.series[' + index + ']',
+ seriesConfig
+ );
+ }
+ seriesConfig = this.plot.get('domainObject').configuration.series[index];
+ // Clone to prevent accidental mutation by ref.
+ seriesConfig = JSON.parse(JSON.stringify(seriesConfig));
+ }
+
+ seriesConfig.persistedConfiguration =
+ this.plot.getPersistedSeriesConfig(domainObject.identifier);
+
+ this.add(new PlotSeries({
+ model: seriesConfig,
+ domainObject: domainObject,
+ collection: this,
+ openmct: this.openmct
+ }));
+ },
+ removeTelemetryObject: function (identifier) {
+ // TODO: properly locate in self (and parent configuration)
+ // Instead of binding via index, which is not guaranteed because
+ // edits could occur when plotcontroller is not instantiated.
+ // This bug also extends to the plotOptions form which currently
+ // relies on indexes that match.
+ var plotObject = this.plot.get('domainObject');
+ if (plotObject.type === 'telemetry.plot.overlay') {
+ var index = _.findIndex(plotObject.configuration.series, function (s) {
+ return _.isEqual(identifier, s.identifier);
+ });
+ this.remove(this.at(index));
+ // 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 () {
+ var newPlotObject = this.plot.get('domainObject');
+ var cSeries = newPlotObject.configuration.series.slice();
+ cSeries.splice(index, 1);
+ this.openmct.objects.mutate(newPlotObject, 'configuration.series', cSeries);
+ }.bind(this));
+ }
+ },
+ onSeriesAdd: function (series) {
+ var 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);
+ var 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) {
+ var seriesIdentifier = series.get('identifier');
+ return seriesIdentifier.namespace === identifier.namespace &&
+ seriesIdentifier.key === identifier.key;
+ })[0];
+ }
+ });
+
+ return SeriesCollection;
+
+});
diff --git a/src/plugins/plot/src/configuration/XAxisModel.js b/src/plugins/plot/src/configuration/XAxisModel.js
new file mode 100644
index 0000000000..e924991aef
--- /dev/null
+++ b/src/plugins/plot/src/configuration/XAxisModel.js
@@ -0,0 +1,89 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ */
+ var XAxisModel = Model.extend({
+ initialize: function (options) {
+ this.plot = options.plot;
+ this.on('change:range', function (newValue, oldValue, model) {
+ if (!model.get('frozen')) {
+ model.set('displayRange', newValue);
+ }
+ });
+
+ this.on('change:frozen', function (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);
+ },
+ changeKey: function (newKey) {
+ var series = this.plot.series.first();
+ if (series) {
+ var xMetadata = series.metadata.value(newKey);
+ var 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);
+ plotSeries.reset();
+ });
+ },
+ defaults: function (options) {
+ var bounds = options.openmct.time.bounds();
+ var timeSystem = options.openmct.time.timeSystem();
+ var 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;
+});
diff --git a/src/plugins/plot/src/configuration/YAxisModel.js b/src/plugins/plot/src/configuration/YAxisModel.js
new file mode 100644
index 0000000000..8660b38d39
--- /dev/null
+++ b/src/plugins/plot/src/configuration/YAxisModel.js
@@ -0,0 +1,219 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *
+ */
+ var 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', function (series) {
+ this.trackSeries(series);
+ this.updateFromSeries(this.seriesCollection);
+ }, this);
+ this.listenTo(this.seriesCollection, 'remove', function (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) {
+ var 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;
+ }
+ var stats = this.get('stats');
+ var 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', function (seriesStats) {
+ if (!seriesStats) {
+ this.resetStats();
+ } else {
+ this.updateStats(seriesStats);
+ }
+ }, this);
+ },
+ untrackSeries: function (series) {
+ this.stopListening(series);
+ this.resetStats();
+ },
+ toggleAutoscale: function (autoscale) {
+ if (autoscale) {
+ 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) {
+ var sampleSeries = series.first();
+ if (!sampleSeries) {
+ return;
+ }
+
+ var yKey = sampleSeries.get('yKey');
+ var yMetadata = sampleSeries.metadata.value(yKey);
+ var yFormat = sampleSeries.formats[yKey];
+ this.set('format', yFormat.format.bind(yFormat));
+ this.set('values', yMetadata.values);
+
+ var plotModel = this.plot.get('domainObject');
+ var label = _.get(plotModel, 'configuration.xAxis.label');
+ if (!label) {
+ var 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;
+ }
+ var 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);
+ this.set('label', labelName);
+ }
+ },
+ defaults: function (options) {
+ return {
+ frozen: false,
+ autoscale: true,
+ autoscalePadding: 0.1
+ };
+ }
+ });
+
+ return YAxisModel;
+
+});
diff --git a/platform/features/plot/test/PlotOptionsFormSpec.js b/src/plugins/plot/src/configuration/configStore.js
similarity index 55%
rename from platform/features/plot/test/PlotOptionsFormSpec.js
rename to src/plugins/plot/src/configuration/configStore.js
index 41811e8be7..c34bed8304 100644
--- a/platform/features/plot/test/PlotOptionsFormSpec.js
+++ b/src/plugins/plot/src/configuration/configStore.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, United States Government
+ * Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -19,29 +19,40 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
+define([
+], function (
+) {
-define(
- ['../src/PlotOptionsForm'],
- function (PlotOptionsForm) {
-
- describe("The Plot Options form", function () {
- var plotOptionsForm;
-
- beforeEach(function () {
-
- plotOptionsForm = new PlotOptionsForm();
- });
-
- it("defines form specs for x-axis, y-axis, and series data", function () {
- expect(plotOptionsForm.xAxisForm).toBeDefined();
- expect(plotOptionsForm.xAxisForm.sections).toBeDefined();
- expect(plotOptionsForm.xAxisForm.sections[0].rows).toBeDefined();
- expect(plotOptionsForm.xAxisForm.sections[0].rows.length).toBeGreaterThan(0);
-
- expect(plotOptionsForm.yAxisForm).toBeDefined();
- expect(plotOptionsForm.plotSeriesForm).toBeDefined();
- });
-
- });
+ function ConfigStore() {
+ this.store = {};
+ this.tracking = {};
}
-);
+
+ ConfigStore.prototype.track = function (id) {
+ if (!this.tracking[id]) {
+ this.tracking[id] = 0;
+ }
+ this.tracking[id] += 1;
+ };
+
+ ConfigStore.prototype.untrack = function (id) {
+ this.tracking[id] -= 1;
+ if (this.tracking[id] <= 0) {
+ delete this.tracking[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];
+ };
+
+ var STORE = new ConfigStore();
+
+ return STORE;
+});
diff --git a/src/plugins/plot/src/draw/Draw2D.js b/src/plugins/plot/src/draw/Draw2D.js
new file mode 100644
index 0000000000..a333529062
--- /dev/null
+++ b/src/plugins/plot/src/draw/Draw2D.js
@@ -0,0 +1,160 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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 (
+
+) {
+
+ /**
+ * 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.");
+ }
+ }
+
+ // 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) {
+ var 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) {
+ var 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) {
+ var x1 = this.x(min[0]),
+ y1 = this.y(min[1]),
+ w = this.x(max[0]) - x1,
+ h = this.y(max[1]) - y1;
+
+ this.setColor(color);
+ this.c2d.fillRect(x1, y1, w, h);
+ };
+
+ Draw2D.prototype.drawPoints = function (
+ buf,
+ color,
+ points,
+ pointSize
+ ) {
+ var i = 0,
+ offset = pointSize / 2;
+
+ this.setColor(color);
+
+ for (; i < points; i++) {
+ this.c2d.fillRect(
+ this.x(buf[i * 2]) - offset,
+ this.y(buf[i * 2 + 1]) - offset,
+ pointSize,
+ 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) {
+ var limitSize = pointSize * 2;
+ var offset = limitSize / 2;
+
+ this.setColor(color);
+
+ for (var i = 0; i < points.length; i++) {
+ this.drawLimitPoint(
+ this.x(points[i].x) - offset,
+ this.y(points[i].y) - offset,
+ limitSize
+ );
+ }
+ };
+
+
+ return Draw2D;
+});
diff --git a/src/plugins/plot/src/draw/DrawLoader.js b/src/plugins/plot/src/draw/DrawLoader.js
new file mode 100644
index 0000000000..c4eb4e1fe6
--- /dev/null
+++ b/src/plugins/plot/src/draw/DrawLoader.js
@@ -0,0 +1,95 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/* global console */
+
+define(
+ [
+ './DrawWebGL',
+ './Draw2D'
+ ],
+ function (DrawWebGL, Draw2D) {
+
+ var CHARTS = [
+ {
+ MAX_INSTANCES: 16,
+ API: DrawWebGL,
+ ALLOCATIONS: []
+ },
+ {
+ MAX_INSTANCES: Number.MAX_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) {
+ var 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;
+ },
+
+ releaseDrawAPI: function (api) {
+ CHARTS.forEach(function (CHART_TYPE) {
+ if (api instanceof CHART_TYPE.API) {
+ CHART_TYPE.ALLOCATIONS.splice(CHART_TYPE.ALLOCATIONS.indexOf(api), 1);
+ }
+ });
+ }
+ };
+ }
+);
diff --git a/src/plugins/plot/src/draw/DrawWebGL.js b/src/plugins/plot/src/draw/DrawWebGL.js
new file mode 100644
index 0000000000..1e44a60a6a
--- /dev/null
+++ b/src/plugins/plot/src/draw/DrawWebGL.js
@@ -0,0 +1,226 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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 (
+
+) {
+
+ // WebGL shader sources (for drawing plain colors)
+ var FRAGMENT_SHADER = [
+ "precision mediump float;",
+ "uniform vec4 uColor;",
+ "void main(void) {",
+ "gl_FragColor = uColor;",
+ "}"
+ ].join('\n'),
+ 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;",
+ "}"
+ ].join('\n');
+
+ /**
+ * 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.");
+ }
+
+ // 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.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();
+
+ // Use a line width of 2.0 for legibility
+ this.gl.lineWidth(2.0);
+
+ // Enable blending, for smoothness
+ this.gl.enable(this.gl.BLEND);
+ this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
+ }
+
+ // 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) {
+ 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.drawArrays(drawType, 0, points);
+ };
+
+ DrawWebGL.prototype.clear = function () {
+ 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 (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) {
+ this.doDraw(this.gl.LINE_STRIP, buf, color, points);
+ };
+
+ /**
+ * Draw the buffer as points.
+ *
+ */
+ DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize) {
+ this.gl.uniform1f(this.uPointSize, pointSize);
+ this.doDraw(this.gl.POINTS, buf, color, points);
+ };
+
+ /**
+ * 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) {
+ 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) {
+ var limitSize = pointSize * 2;
+ var offset = limitSize / 2;
+
+ var 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 (var i = 0; i < points.length; i++) {
+ this.drawLimitPoint(
+ this.x(points[i].x) - offset,
+ this.y(points[i].y) - offset,
+ limitSize
+ );
+ }
+ };
+
+ return DrawWebGL;
+});
diff --git a/src/plugins/plot/src/inspector/HideElementPoolDirective.js b/src/plugins/plot/src/inspector/HideElementPoolDirective.js
new file mode 100644
index 0000000000..60932dc66f
--- /dev/null
+++ b/src/plugins/plot/src/inspector/HideElementPoolDirective.js
@@ -0,0 +1,55 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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) {
+ var splitter = $element.parent();
+
+ while (splitter[0].tagName !== 'MCT-SPLIT-PANE') {
+ splitter = splitter.parent();
+ }
+
+ [
+ '.split-pane-component.pane.bottom',
+ 'mct-splitter'
+ ].forEach(function (selector) {
+ var 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';
+ }
+ };
+ };
+});
diff --git a/src/plugins/plot/src/inspector/InspectorRegion.js b/src/plugins/plot/src/inspector/InspectorRegion.js
new file mode 100644
index 0000000000..a0fd87806a
--- /dev/null
+++ b/src/plugins/plot/src/inspector/InspectorRegion.js
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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 () {
+ var 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;
+ }
+);
diff --git a/src/plugins/plot/src/inspector/PlotBrowseRegion.js b/src/plugins/plot/src/inspector/PlotBrowseRegion.js
new file mode 100644
index 0000000000..98985efe60
--- /dev/null
+++ b/src/plugins/plot/src/inspector/PlotBrowseRegion.js
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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
+) {
+
+ var PlotBrowseRegion = new Region({
+ name: "plot-options",
+ title: "Plot Options",
+ modes: ['browse'],
+ content: {
+ key: "plot-options-browse"
+ }
+ });
+
+ return PlotBrowseRegion;
+
+});
diff --git a/src/plugins/plot/src/inspector/PlotEditRegion.js b/src/plugins/plot/src/inspector/PlotEditRegion.js
new file mode 100644
index 0000000000..8ba27ff0ed
--- /dev/null
+++ b/src/plugins/plot/src/inspector/PlotEditRegion.js
@@ -0,0 +1,18 @@
+/*global define*/
+define([
+ './Region'
+], function (
+ Region
+) {
+
+ var PlotEditRegion = new Region({
+ name: "plot-options",
+ title: "Plot Options",
+ modes: ['edit'],
+ content: {
+ key: "plot-options-edit"
+ }
+ });
+
+ return PlotEditRegion;
+});
diff --git a/src/plugins/plot/src/inspector/PlotInspector.js b/src/plugins/plot/src/inspector/PlotInspector.js
new file mode 100644
index 0000000000..faa615af1e
--- /dev/null
+++ b/src/plugins/plot/src/inspector/PlotInspector.js
@@ -0,0 +1,39 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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
+) {
+
+ var plotInspector = new InspectorRegion();
+
+ plotInspector.addRegion(PlotBrowseRegion);
+ plotInspector.addRegion(PlotEditRegion);
+
+ return plotInspector;
+});
diff --git a/src/plugins/plot/src/inspector/PlotOptionsController.js b/src/plugins/plot/src/inspector/PlotOptionsController.js
new file mode 100644
index 0000000000..e57a929000
--- /dev/null
+++ b/src/plugins/plot/src/inspector/PlotOptionsController.js
@@ -0,0 +1,258 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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',
+ '../../../../api/objects/object-utils',
+ 'lodash'
+], function (
+ configStore,
+ eventHelpers,
+ objectUtils,
+ _
+) {
+
+ /**
+ * The LayoutController is responsible for supporting the
+ * Layout view. It arranges frames according to saved configuration
+ * and provides methods for updating these based on mouse
+ * movement.
+ * @memberof platform/features/plot
+ * @constructor
+ * @param {Scope} $scope the controller's Angular scope
+ */
+ 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.setColor = function (series, color) {
+ var seriesWithColor = this.config.series.filter(function (s) {
+ return s.get('color') === color;
+ })[0];
+ var oldColor = series.get('color');
+ series.set('color', color);
+
+ var seriesIndex = this.config.series.indexOf(series);
+ this.openmct.objects.mutate(
+ this.domainObject,
+ 'configuration.series[' + seriesIndex + '].color',
+ color.asHexString()
+ );
+
+ if (seriesWithColor) {
+ seriesWithColor.set('color', oldColor);
+ var oldSeriesIndex = this.config.series.indexOf(seriesWithColor);
+ this.openmct.objects.mutate(
+ this.domainObject,
+ 'configuration.series[' + oldSeriesIndex + '].color',
+ oldColor.asHexString()
+ );
+ }
+ };
+
+ PlotOptionsController.prototype.updateDomainObject = function (domainObject) {
+ this.domainObject = domainObject;
+ };
+
+ PlotOptionsController.prototype.destroy = function () {
+ configStore.untrack(this.configId);
+ this.stopListening();
+ this.unlisten();
+ };
+
+ PlotOptionsController.prototype.setUpScope = function () {
+ var config = configStore.get(this.configId);
+ if (!config) {
+ this.$timeout(this.setUpScope.bind(this));
+ return;
+ }
+ configStore.track(this.configId);
+
+ this.config = this.$scope.config = config;
+ this.$scope.setColor = this.setColor.bind(this);
+ this.$scope.form = {series: []};
+ this.$scope.validation = {};
+
+ this.domainObject = 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);
+
+ this.linkFields(config.yAxis, 'label', 'form.yAxis.label', undefined, undefined, 'configuration.yAxis.label');
+ this.linkFields(config.yAxis, 'autoscale', 'form.yAxis.autoscale', Boolean, undefined, 'configuration.yAxis.autoscale');
+ this.linkFields(config.yAxis, 'autoscalePadding', 'form.yAxis.autoscalePadding', Number, undefined, 'configuration.yAxis.autoscalePadding');
+ this.linkFields(config.yAxis, 'range', 'form.yAxis.range', function coerceRange(range) {
+ if (!range) {
+ return {
+ min: 0,
+ max: 0
+ };
+ }
+ var newRange = {};
+ if (typeof range.min !== undefined) {
+ newRange.min = Number(range.min);
+ }
+ if (typeof range.max !== undefined) {
+ newRange.max = Number(range.max);
+ }
+ return newRange;
+ }, function validateRange(range) {
+ if (!range) {
+ return 'Need range';
+ }
+ if (!range.min) {
+ return 'Must specify Minimum';
+ }
+ if (!range.max) {
+ return 'Must specify Maximum';
+ }
+ if (_.isNaN(Number(range.min))) {
+ return 'Minimum must be a number.';
+ }
+ if (_.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 (config.yAxis.get('autoscale')) {
+ return false;
+ }
+ return true;
+ }, 'configuration.yAxis.range');
+
+ this.linkFields(config.legend, 'position', 'form.legend.position', undefined, undefined, 'configuration.legend.position');
+ this.linkFields(config.legend, 'expandByDefault', 'form.legend.expandByDefault', Boolean, undefined, 'configuration.legend.expandByDefault');
+ this.linkFields(config.legend, 'valueToShowWhenCollapsed', 'form.legend.valueToShowWhenCollapsed', undefined, undefined, 'configuration.legend.valueToShowWhenCollapsed');
+ this.linkFields(config.legend, 'showValueWhenExpanded', 'form.legend.showValueWhenExpanded', Boolean, undefined, 'configuration.legend.showValueWhenExpanded');
+ this.linkFields(config.legend, 'showTimestampWhenExpanded', 'form.legend.showTimestampWhenExpanded', Boolean, undefined, 'configuration.legend.showTimestampWhenExpanded');
+ this.linkFields(config.legend, 'showMaximumWhenExpanded', 'form.legend.showMaximumWhenExpanded', Boolean, undefined, 'configuration.legend.showMaximumWhenExpanded');
+ this.linkFields(config.legend, 'showMinimumWhenExpanded', 'form.legend.showMinimumWhenExpanded', Boolean, undefined, 'configuration.legend.showMinimumWhenExpanded');
+ };
+
+ PlotOptionsController.prototype.addSeries = function (series, index) {
+ if (this.$scope.form.series[index]) {
+ // the way listeners work, this will blow up. At this point, it
+ // can't technically occur, but if we added some sort of reordering
+ // at a later date, it would not be clear why this doesn't work.
+ // So here's to hoping this helps debugging.
+ throw new Error('Plot options does not support insert at index.');
+ }
+ var metadata = series.metadata;
+ this.$scope.form.series[index] = {
+ yAxisOptions: metadata.valuesForHints(['range']).map(function (o) {
+ return {
+ name: o.key,
+ value: o.key
+ };
+ })
+ };
+ var seriesObject = series.domainObject;
+ var seriesId = objectUtils.makeKeyString(seriesObject.identifier);
+ var configPath = 'configuration.series[' + index + '].';
+ var path = 'form.series[' + index + '].';
+ this.$scope.domainObject.useCapability('composition')
+ .then(function (children) {
+ children.forEach(function (child) {
+ if (child.getId() === seriesId) {
+ series.oldObject = child;
+ }
+ });
+ }.bind(this));
+
+ this.linkFields(series, 'yKey', path + 'yKey', undefined, undefined, configPath + 'yKey');
+ this.linkFields(series, 'interpolate', path + 'interpolate', undefined, undefined, configPath + 'interpolate');
+ this.linkFields(series, 'markers', path + 'markers', undefined, undefined, configPath + 'markers');
+ this.linkFields(series, 'markerSize', path + 'markerSize', Number, undefined, configPath + 'markerSize');
+ this.linkFields(series, 'alarmMarkers', path + 'alarmMarkers', Boolean, undefined, configPath + 'alarmMarkers');
+ };
+
+ PlotOptionsController.prototype.resetAllSeries = function (series, index) {
+ this.removeSeries(series);
+ this.config.series.forEach(this.removeSeries, this);
+ this.$scope.form.series = [];
+ this.config.series.forEach(this.addSeries, this);
+ };
+
+ PlotOptionsController.prototype.removeSeries = function (series) {
+ this.stopListening(series);
+ series.stopListening(this.$scope);
+ };
+
+ PlotOptionsController.prototype.linkFields = function (
+ model,
+ prop,
+ scopePath,
+ coerce,
+ validate,
+ objectPath
+ ) {
+ if (!coerce) {
+ coerce = function (v) {
+ return v;
+ };
+ }
+ if (!validate) {
+ validate = function () {
+ return true;
+ };
+ }
+ this.listenTo(model, 'change:' + prop, function (newVal, oldVal) {
+ if (!_.isEqual(coerce(_.get(this.$scope, scopePath)), coerce(newVal))) {
+ _.set(this.$scope, scopePath, coerce(newVal));
+ }
+ }, this);
+ model.listenTo(this.$scope, 'change:' + scopePath, function (newVal, oldVal) {
+ if (_.isEqual(coerce(newVal), coerce(model.get(prop)))) {
+ return; // Don't trigger excessive mutations.
+ }
+ var validationResult = validate(newVal);
+ if (validationResult === true) {
+ delete this.$scope.validation[scopePath];
+ } else {
+ this.$scope.validation[scopePath] = validationResult;
+ return;
+ }
+ if (!_.isEqual(coerce(newVal), coerce(oldVal))) {
+ model.set(prop, coerce(newVal));
+ if (objectPath && this.$scope.domainObject.getCapability('editor').isEditContextRoot()) {
+ this.openmct.objects.mutate(this.domainObject, objectPath, coerce(newVal));
+ }
+ }
+ }, this);
+ _.set(this.$scope, scopePath, coerce(model.get(prop)));
+ };
+
+ return PlotOptionsController;
+});
+
diff --git a/src/plugins/plot/src/inspector/PlotOptionsForm.js b/src/plugins/plot/src/inspector/PlotOptionsForm.js
new file mode 100644
index 0000000000..2c377cf88c
--- /dev/null
+++ b/src/plugins/plot/src/inspector/PlotOptionsForm.js
@@ -0,0 +1,177 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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 () {
+
+
+ /**
+ * A class for encapsulating structure and behaviour of the plot
+ * options form
+ * @memberOf platform/features/plot
+ * @param topic
+ * @constructor
+ */
+ function PlotOptionsForm() {
+
+ /*
+ Defined below are the form structures for the plot options.
+ */
+ this.xAxisForm = {
+ name: 'x-axis',
+ sections: [{
+ name: 'x-axis',
+ rows: [
+ {
+ name: 'Domain',
+ control: 'select',
+ key: 'key',
+ options: [
+ {
+ name: 'SCET',
+ value: 'scet'
+ },
+ {
+ name: 'ERT',
+ value: 'ert'
+ },
+ {
+ name: 'SCLK',
+ value: 'sclk'
+ },
+ {
+ name: 'LST',
+ value: 'lst'
+ }
+ ]
+ }
+ ]
+ }]};
+
+ this.yAxisForm = {
+ name: 'y-axis',
+ sections: [{
+ // Will need to be repeated for each y-axis, with a
+ // distinct name for each. Ideally the name of the axis
+ // itself.
+ name: 'y-axis',
+ rows: [
+ {
+ name: 'Range',
+ control: 'select',
+ key: 'key',
+ options: [
+ {
+ name: 'Autoselect',
+ value: 'auto'
+ },
+ {
+ name: 'EU',
+ value: 'eu'
+ },
+ {
+ name: 'DN',
+ value: 'dn'
+ },
+ {
+ name: 'Status',
+ value: 'enum'
+ }
+ ]
+ },
+ {
+ name: 'Autoscale',
+ control: 'checkbox',
+ key: 'autoscale'
+ },
+ {
+ name: 'Min',
+ control: 'textfield',
+ key: 'min',
+ pattern: '[0-9]',
+ inputsize: 'sm'
+ },
+ {
+ name: 'Max',
+ control: 'textfield',
+ key: 'max',
+ pattern: '[0-9]',
+ inputsize: 'sm'
+ }
+ ]
+ }]
+ };
+ this.plotSeriesForm = {
+ name: 'Series Options',
+ sections: [
+ {
+ rows: [
+ {
+ name: 'Color',
+ control: 'color',
+ key: 'color'
+ }]
+ },
+ {
+ rows: [
+ {
+ name: 'Markers',
+ control: 'checkbox',
+ key: 'markers',
+ layout: 'control-first'
+ }
+ ]
+ },
+ {
+ rows: [
+ {
+ name: 'No Line',
+ control: 'radio',
+ key: 'interpolate',
+ value: 'none',
+ layout: 'control-first'
+ },
+ {
+ name: 'Step Line',
+ control: 'radio',
+ key: 'interpolate',
+ value: 'stepAfter',
+ layout: 'control-first'
+ },
+ {
+ name: 'Linear Line',
+ control: 'radio',
+ key: 'interpolate',
+ value: 'linear',
+ layout: 'control-first'
+ }
+ ]
+ }
+ ]
+ };
+ }
+
+ return PlotOptionsForm;
+});
+
diff --git a/src/plugins/plot/src/inspector/Region.js b/src/plugins/plot/src/inspector/Region.js
new file mode 100644
index 0000000000..f9364a892c
--- /dev/null
+++ b/src/plugins/plot/src/inspector/Region.js
@@ -0,0 +1,100 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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} [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;
+ }
+);
diff --git a/src/plugins/plot/src/lib/color.js b/src/plugins/plot/src/lib/color.js
new file mode 100644
index 0000000000..55e8967010
--- /dev/null
+++ b/src/plugins/plot/src/lib/color.js
@@ -0,0 +1,205 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.
+ *****************************************************************************/
+/* global console */
+
+define(function () {
+
+ var 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) {
+ var 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() {
+ var allColors = this.allColors = COLOR_PALETTE.map(function (color) {
+ return new Color(color);
+ });
+ this.colorGroups = [[], [], []];
+ for (var 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) {
+ var 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
+ };
+});
diff --git a/src/plugins/plot/src/lib/eventHelpers.js b/src/plugins/plot/src/lib/eventHelpers.js
new file mode 100644
index 0000000000..e464a8e33f
--- /dev/null
+++ b/src/plugins/plot/src/lib/eventHelpers.js
@@ -0,0 +1,97 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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 (
+
+) {
+
+ var helperFunctions = {
+ listenTo: function (object, event, callback, context) {
+ if (!this._listeningTo) {
+ this._listeningTo = [];
+ }
+ var listener = {
+ object: object,
+ event: event,
+ callback: callback,
+ context: context,
+ _cb: !!context ? callback.bind(context) : callback
+ };
+ if (object.$watch && event.indexOf('change:') === 0) {
+ var 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;
+});
diff --git a/src/plugins/plot/src/lib/extend.js b/src/plugins/plot/src/lib/extend.js
new file mode 100644
index 0000000000..a8b7da3ccb
--- /dev/null
+++ b/src/plugins/plot/src/lib/extend.js
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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) {
+ /*jshint validthis: true*/
+ var parent = this,
+ child,
+ Surrogate;
+
+ if (props && props.hasOwnProperty('constructor')) {
+ child = props.constructor;
+ } else {
+ child = function () {
+ 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;
+});
diff --git a/src/plugins/plot/src/plot/LinearScale.js b/src/plugins/plot/src/plot/LinearScale.js
new file mode 100644
index 0000000000..1fa7a85d11
--- /dev/null
+++ b/src/plugins/plot/src/plot/LinearScale.js
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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;
+ }
+ var domainOffset = domainValue - this._domain.min,
+ rangeFraction = domainOffset - this._domainDenominator,
+ rangeOffset = rangeFraction * this._rangeDenominator,
+ rangeValue = rangeOffset + this._range.min;
+ return rangeValue;
+ };
+ LinearScale.prototype.invert = function (rangeValue) {
+ if (!this._domain || !this._range) {
+ return;
+ }
+ var rangeOffset = rangeValue - this._range.min,
+ domainFraction = rangeOffset / this._rangeDenominator,
+ domainOffset = domainFraction * this._domainDenominator,
+ domainValue = domainOffset + this._domain.min;
+ return domainValue;
+ };
+ return LinearScale;
+});
+
+/**
+ *
+ */
diff --git a/src/plugins/plot/src/plot/MCTPlotController.js b/src/plugins/plot/src/plot/MCTPlotController.js
new file mode 100644
index 0000000000..27268c62f2
--- /dev/null
+++ b/src/plugins/plot/src/plot/MCTPlotController.js
@@ -0,0 +1,339 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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.$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.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.watchForMarquee();
+
+ this.listenTo(this.$window, 'keydown', this.toggleInteractionMode, this);
+ this.listenTo(this.$window, 'keyup', this.resetInteractionMode, 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.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.config.xAxis, 'change:displayRange', this.onXAxisChange, this);
+ this.listenTo(this.config.yAxis, 'change:displayRange', this.onYAxisChange, this);
+ };
+
+ 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.
+ var 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)
+ };
+
+ this.highlightValues(this.positionOverPlot.x);
+ this.updateMarquee();
+ this.updatePan();
+ this.$scope.$digest();
+ $event.preventDefault();
+ };
+
+ 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 (!point) {
+ this.$scope.highlights = [];
+ this.$scope.series.map(function (series) {
+ delete series.closest;
+ });
+ } else {
+ this.$scope.highlights = this.$scope.series
+ .filter(function (series) {
+ return series.data.length > 0;
+ }).map(function (series) {
+ series.closest = series.nearestPoint(point);
+ return {
+ series: series,
+ point: series.closest
+ };
+ }, this);
+ }
+ this.$scope.$digest();
+ };
+
+ MCTPlotController.prototype.untrackMousePosition = function () {
+ this.positionOverElement = undefined;
+ this.positionOverPlot = undefined;
+ this.highlightValues();
+ };
+
+ MCTPlotController.prototype.onMouseDown = function ($event) {
+ this.listenTo(this.$window, 'mouseup', this.onMouseUp, this);
+ this.listenTo(this.$window, 'mousemove', this.trackMousePosition, this);
+ if (this.allowPan) {
+ return this.startPan($event);
+ }
+ if (this.allowMarquee) {
+ 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.pan) {
+ this.endPan($event);
+ }
+ if (this.marquee) {
+ this.endMarquee($event);
+ }
+ this.$scope.$apply();
+ };
+
+ MCTPlotController.prototype.updateMarquee = function () {
+ if (!this.marquee) {
+ return;
+ }
+ this.marquee.end = this.positionOverPlot;
+ };
+
+ MCTPlotController.prototype.startMarquee = function ($event) {
+ this.trackMousePosition($event);
+ if (this.positionOverPlot) {
+ this.freeze();
+ this.marquee = {
+ start: this.positionOverPlot,
+ end: this.positionOverPlot,
+ color: [1, 1, 1, 0.5]
+ };
+ this.$scope.rectangles.push(this.marquee);
+ this.trackHistory();
+ }
+ };
+
+ MCTPlotController.prototype.endMarquee = function () {
+ if (this.marquee.start.x !== this.marquee.end.x &&
+ this.marquee.start.y !== this.marquee.end.y) {
+ 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');
+ }
+ this.$scope.rectangles = [];
+ this.marquee = undefined;
+ };
+
+ MCTPlotController.prototype.startPan = function ($event) {
+ 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;
+ }
+ var dX = this.pan.start.x - this.positionOverPlot.x,
+ dY = this.pan.start.y - this.positionOverPlot.y,
+ xRange = this.config.xAxis.get('displayRange'),
+ 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.watchForMarquee = function () {
+ this.$canvas.removeClass('plot-drag');
+ this.$canvas.addClass('plot-marquee');
+ this.allowPan = false;
+ this.allowMarquee = true;
+ };
+
+ MCTPlotController.prototype.watchForPan = function () {
+ this.$canvas.addClass('plot-drag');
+ this.$canvas.removeClass('plot-marquee');
+ this.allowPan = true;
+ this.allowMarquee = false;
+ };
+
+ MCTPlotController.prototype.toggleInteractionMode = function (event) {
+ if (event.keyCode === 18) { // control key.
+ this.watchForPan();
+ }
+ };
+
+ MCTPlotController.prototype.resetInteractionMode = function (event) {
+ if (event.keyCode === 18) {
+ this.watchForMarquee();
+ }
+ };
+
+ 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 () {
+ var 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();
+ };
+
+ return MCTPlotController;
+});
diff --git a/src/plugins/plot/src/plot/MCTPlotDirective.js b/src/plugins/plot/src/plot/MCTPlotDirective.js
new file mode 100644
index 0000000000..2d1e155e1b
--- /dev/null
+++ b/src/plugins/plot/src/plot/MCTPlotDirective.js
@@ -0,0 +1,46 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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',
+ 'text!../../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;
+});
diff --git a/src/plugins/plot/src/plot/MCTTicksController.js b/src/plugins/plot/src/plot/MCTTicksController.js
new file mode 100644
index 0000000000..5ae128cd12
--- /dev/null
+++ b/src/plugins/plot/src/plot/MCTTicksController.js
@@ -0,0 +1,248 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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
+) {
+
+ var e10 = Math.sqrt(50),
+ e5 = Math.sqrt(10),
+ e2 = Math.sqrt(2);
+
+ /**
+ * Nicely formatted tick steps from d3-array.
+ */
+ function tickStep(start, stop, count) {
+ var step0 = Math.abs(stop - start) / Math.max(0, count),
+ step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
+ 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) {
+ var exponential = step.toExponential(),
+ i = exponential.indexOf('e');
+ if (i === -1) {
+ return 0;
+ }
+
+ var precision = Math.max(0, -(+exponential.slice(i + 1)));
+
+ if (precision > 20) {
+ precision = 20;
+ }
+
+ return precision;
+ }
+
+
+ /**
+ * Linear tick generation from d3-array.
+ */
+ function ticks(start, stop, count) {
+ var step = tickStep(start, stop, count),
+ precision = getPrecision(step);
+ return _.range(
+ Math.ceil(start / step) * step,
+ Math.floor(stop / step) * step + step / 2, // inclusive
+ step
+ ).map(function round(tick) {
+ return +tick.toFixed(precision);
+ });
+ }
+
+ function commonPrefix(a, b) {
+ var maxLen = Math.min(a.length, b.length);
+ var breakpoint = 0;
+ for (var 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) {
+ var maxLen = Math.min(a.length, b.length);
+ var breakpoint = 0;
+ for (var 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.$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.$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 the existing ticks are
+ * outside of given range, or c) if the range exceeds the size of the tick
+ * range by more than one tick step.
+ * @private
+ */
+ MCTTicksController.prototype.shouldRegenerateTicks = function (range) {
+ 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 () {
+ var number = this.tickCount;
+ var clampRange = this.axis.get('values');
+ var 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.updateTicks = function () {
+ var 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;
+ }
+ var 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)) {
+ var 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') {
+ var tickText = newTicks.map(function (t) {
+ return t.text;
+ });
+ var prefix = tickText.reduce(commonPrefix);
+ var 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();
+ var element = this.$element[0],
+ tickElements = element.querySelectorAll('.gl-plot-tick > span'),
+ 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;
+});
diff --git a/src/plugins/plot/src/plot/MCTTicksDirective.js b/src/plugins/plot/src/plot/MCTTicksDirective.js
new file mode 100644
index 0000000000..3e849cf0eb
--- /dev/null
+++ b/src/plugins/plot/src/plot/MCTTicksDirective.js
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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;
+});
diff --git a/platform/features/plot/src/services/ExportImageService.js b/src/plugins/plot/src/services/ExportImageService.js
similarity index 75%
rename from platform/features/plot/src/services/ExportImageService.js
rename to src/plugins/plot/src/services/ExportImageService.js
index 7738d47224..5c3a6bd2c6 100644
--- a/platform/features/plot/src/services/ExportImageService.js
+++ b/src/plugins/plot/src/services/ExportImageService.js
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Open MCT, Copyright (c) 2014-2017, United States Government
+ * Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -32,7 +32,6 @@ define(
html2canvas,
saveAs
) {
- var self = this;
/**
* The export image service will export any HTML node to
@@ -43,15 +42,15 @@ define(
* @param {constant} EXPORT_IMAGE_TIMEOUT time in milliseconds before a timeout error is returned
* @constructor
*/
- function ExportImageService($q, $timeout, $log, EXPORT_IMAGE_TIMEOUT, injHtml2Canvas, injSaveAs, injFileReader, injChangeBackgroundColor) {
- self.$q = $q;
- self.$timeout = $timeout;
- self.$log = $log;
- self.EXPORT_IMAGE_TIMEOUT = EXPORT_IMAGE_TIMEOUT;
- self.html2canvas = injHtml2Canvas || html2canvas;
- self.saveAs = injSaveAs || saveAs;
- self.reader = injFileReader || new FileReader();
- self.changeBackgroundColor = injChangeBackgroundColor || self.changeBackgroundColor;
+ function ExportImageService($q, $timeout, $log) {
+ this.$q = $q;
+ this.$timeout = $timeout;
+ this.$log = $log;
+ this.EXPORT_IMAGE_TIMEOUT = 1000;
+ }
+
+ function changeBackgroundColor(element, color) {
+ element.style.backgroundColor = color;
}
/**
@@ -61,42 +60,42 @@ define(
* @param {string} type of image to convert the element to
* @returns {promise}
*/
- function renderElement(element, type, color) {
- var defer = self.$q.defer(),
+ ExportImageService.prototype.renderElement = function (element, type, color) {
+ var defer = this.$q.defer(),
validTypes = ["png", "jpg", "jpeg"],
renderTimeout,
originalColor;
if (validTypes.indexOf(type) === -1) {
- self.$log.error("Invalid type requested. Try: (" + validTypes.join(",") + ")");
+ this.$log.error("Invalid type requested. Try: (" + validTypes.join(",") + ")");
return;
}
if (color) {
// Save color to be restored later
originalColor = element.style.backgroundColor || '';
-
// Defaulting to white so we can see the chart when printed
- self.changeBackgroundColor(element, color);
+ changeBackgroundColor(element, color);
}
- renderTimeout = self.$timeout(function () {
+ renderTimeout = this.$timeout(function () {
defer.reject("html2canvas timed out");
- self.$log.warn("html2canvas timed out");
- }, self.EXPORT_IMAGE_TIMEOUT);
+ this.$log.warn("html2canvas timed out");
+ }.bind(this), this.EXPORT_IMAGE_TIMEOUT);
try {
- self.html2canvas(element, {
+ html2canvas(element, {
onrendered: function (canvas) {
if (color) {
- self.changeBackgroundColor(element, originalColor);
+ changeBackgroundColor(element, originalColor);
}
-
switch (type.toLowerCase()) {
case "png":
canvas.toBlob(defer.resolve, "image/png");
break;
+ default:
+ case "jpg":
case "jpeg":
canvas.toBlob(defer.resolve, "image/jpeg");
break;
@@ -105,19 +104,42 @@ define(
});
} catch (e) {
defer.reject(e);
- self.$log.warn("html2canvas failed with error: " + e);
+ this.$log.warn("html2canvas failed with error: " + e);
}
defer.promise.finally(function () {
renderTimeout.cancel();
-
if (color) {
- self.changeBackgroundColor(element, originalColor);
+ changeBackgroundColor(element, originalColor);
}
});
return defer.promise;
- }
+ };
+
+ /**
+ * Takes a screenshot of a DOM node and exports to JPG.
+ * @param {node} element to be exported
+ * @param {string} filename the exported image
+ * @returns {promise}
+ */
+ ExportImageService.prototype.exportJPG = function (element, filename, color) {
+ return this.renderElement(element, "jpeg", color).then(function (img) {
+ saveAs(img, filename);
+ });
+ };
+
+ /**
+ * Takes a screenshot of a DOM node and exports to PNG.
+ * @param {node} element to be exported
+ * @param {string} filename the exported image
+ * @returns {promise}
+ */
+ ExportImageService.prototype.exportPNG = function (element, filename, color) {
+ return this.renderElement(element, "png", color).then(function (img) {
+ saveAs(img, filename);
+ });
+ };
/**
* canvas.toBlob() not supported in IE < 10, Opera, and Safari. This polyfill
@@ -143,37 +165,6 @@ define(
}
}
- /**
- * @private
- */
- self.changeBackgroundColor = function (element, color) {
- element.style.backgroundColor = color;
- };
-
- /**
- * Takes a screenshot of a DOM node and exports to JPG.
- * @param {node} element to be exported
- * @param {string} filename the exported image
- * @returns {promise}
- */
- ExportImageService.prototype.exportJPG = function (element, filename, color) {
- return renderElement(element, "jpeg", color).then(function (img) {
- self.saveAs(img, filename);
- });
- };
-
- /**
- * Takes a screenshot of a DOM node and exports to PNG.
- * @param {node} element to be exported
- * @param {string} filename the exported image
- * @returns {promise}
- */
- ExportImageService.prototype.exportPNG = function (element, filename, color) {
- return renderElement(element, "png", color).then(function (img) {
- self.saveAs(img, filename);
- });
- };
-
polyfillToBlob();
return ExportImageService;
diff --git a/src/plugins/plot/src/telemetry/MCTOverlayPlot.js b/src/plugins/plot/src/telemetry/MCTOverlayPlot.js
new file mode 100644
index 0000000000..dcebe96d09
--- /dev/null
+++ b/src/plugins/plot/src/telemetry/MCTOverlayPlot.js
@@ -0,0 +1,37 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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([
+ 'text!../../res/templates/plot.html'
+], function (
+ PlotTemplate
+) {
+ return function MCTOverlayPlot() {
+ return {
+ restrict: "E",
+ template: PlotTemplate,
+ scope: {
+ domainObject: "="
+ }
+ };
+ };
+});
diff --git a/src/plugins/plot/src/telemetry/PlotController.js b/src/plugins/plot/src/telemetry/PlotController.js
new file mode 100644
index 0000000000..e3ca0d34bc
--- /dev/null
+++ b/src/plugins/plot/src/telemetry/PlotController.js
@@ -0,0 +1,239 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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;
+
+ $scope.pending = 0;
+
+ this.listenTo($scope, 'user:viewport:change:end', this.onUserViewportChangeEnd, this);
+ this.listenTo($scope, '$destroy', this.destroy, this);
+
+ 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();
+ }
+
+ eventHelpers.extend(PlotController.prototype);
+
+ PlotController.prototype.followTimeConductor = function () {
+ this.listenTo(this.openmct.time, 'bounds', this.updateDisplayBounds, this);
+ this.listenTo(this.openmct.time, 'timeSystem', this.onTimeSystemChange, this);
+ this.synchronized(true);
+ };
+
+ PlotController.prototype.loadSeriesData = function (series) {
+ this.startLoading();
+ var options = {
+ size: this.$element[0].offsetWidth,
+ domain: this.config.xAxis.get('key')
+ };
+
+ series.load(options)
+ .then(this.stopLoading.bind(this));
+ };
+
+ PlotController.prototype.addSeries = function (series) {
+ this.listenTo(series, 'change:yKey', function () {
+ this.loadSeriesData(series);
+ }, this);
+ this.loadSeriesData(series);
+ };
+
+ PlotController.prototype.removeSeries = function (plotSeries) {
+ this.stopListening(plotSeries);
+ };
+
+ PlotController.prototype.getConfig = function (domainObject) {
+ var configId = domainObject.getId();
+ var config = configStore.get(configId);
+ if (!config) {
+ var newDomainObject = domainObject.useCapability('adapter');
+ config = new PlotConfigurationModel({
+ id: configId,
+ domainObject: newDomainObject,
+ openmct: this.openmct
+ });
+ configStore.add(configId, config);
+ }
+ configStore.track(configId);
+ return config;
+ };
+
+ PlotController.prototype.onTimeSystemChange = function (timeSystem) {
+ this.config.xAxis.set('key', timeSystem.key);
+ };
+
+ PlotController.prototype.destroy = function () {
+ configStore.untrack(this.config.id);
+ this.stopListening();
+ };
+
+ PlotController.prototype.loadMoreData = function (range, purge) {
+ this.config.series.map(function (plotSeries) {
+ this.startLoading();
+ plotSeries.load({
+ size: this.$element[0].offsetWidth,
+ start: range.min,
+ end: range.max
+ })
+ .then(this.stopLoading.bind(this));
+ if (purge) {
+ plotSeries.purgeRecordsOutsideRange(range);
+ }
+ }, this);
+ };
+
+ /**
+ * Track latest display bounds. Forces update when not receiving ticks.
+ */
+ PlotController.prototype.updateDisplayBounds = function (bounds, isTick) {
+ var newRange = {
+ min: bounds.start,
+ max: bounds.end
+ };
+ this.config.xAxis.set('range', newRange);
+ if (!isTick) {
+ this.$scope.$broadcast('plot:clearHistory');
+ 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()) {
+ var 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.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;
+ var 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 () {
+ var xDisplayRange = this.config.xAxis.get('displayRange');
+ var xRange = this.config.xAxis.get('range');
+
+ this.loadMoreData(xDisplayRange);
+
+ this.synchronized(xRange.min === xDisplayRange.min &&
+ xRange.max === xDisplayRange.max);
+ };
+
+ /**
+ * Export view as JPG.
+ */
+ PlotController.prototype.exportJPG = function () {
+ this.hideExportButtons = true;
+ this.exportImageService.exportJPG(this.$element[0], 'plot.jpg', 'white')
+ .finally(function () {
+ this.hideExportButtons = false;
+ }.bind(this));
+ };
+
+ /**
+ * Export view as PNG.
+ */
+ PlotController.prototype.exportPNG = function () {
+ this.hideExportButtons = true;
+ this.exportImageService.exportPNG(this.$element[0], 'plot.png', 'white')
+ .finally(function () {
+ this.hideExportButtons = false;
+ }.bind(this));
+ };
+
+ return PlotController;
+
+});
diff --git a/src/plugins/plot/src/telemetry/StackedPlotController.js b/src/plugins/plot/src/telemetry/StackedPlotController.js
new file mode 100644
index 0000000000..95b3513b1d
--- /dev/null
+++ b/src/plugins/plot/src/telemetry/StackedPlotController.js
@@ -0,0 +1,146 @@
+/*****************************************************************************
+ * Open MCT, Copyright (c) 2014-2018, 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'
+], function (
+ _
+) {
+
+ function StackedPlotController($scope, openmct, objectService, $element, exportImageService) {
+ var tickWidth = 0,
+ newFormatObject,
+ composition,
+ currentRequest,
+ unlisten,
+ tickWidthMap = {};
+
+ this.$element = $element;
+ this.exportImageService = exportImageService;
+
+ $scope.telemetryObjects = [];
+
+ function oldId(newIdentifier) {
+ var idParts = [];
+ if (newIdentifier.namespace) {
+ idParts.push(newIdentifier.namespace.replace(/\:/g, '\\:'));
+ }
+ idParts.push(newIdentifier.key);
+ return idParts.join(':');
+ }
+
+ function onDomainObjectChange(domainObject) {
+ var thisRequest = {
+ pending: 0
+ };
+ currentRequest = thisRequest;
+ $scope.currentRequest = thisRequest;
+ var telemetryObjects = $scope.telemetryObjects = [];
+ var thisTickWidthMap = {};
+ tickWidthMap = thisTickWidthMap;
+
+ if (unlisten) {
+ unlisten();
+ unlisten = undefined;
+ }
+
+ function addChild(child) {
+ var id = oldId(child.identifier);
+ thisTickWidthMap[id] = 0;
+ thisRequest.pending += 1;
+ objectService.getObjects([id])
+ .then(function (objects) {
+ thisRequest.pending -= 1;
+ var childObj = objects[id];
+ telemetryObjects.push(childObj);
+ });
+ }
+
+ function removeChild(childIdentifier) {
+ var id = oldId(childIdentifier);
+ delete thisTickWidthMap[id];
+ var childObj = telemetryObjects.filter(function (c) {
+ return c.getId() === id;
+ })[0];
+ if (childObj) {
+ var index = telemetryObjects.indexOf(childObj);
+ telemetryObjects.splice(index, 1);
+ $scope.$broadcast('plot:tickWidth', _.max(tickWidthMap));
+ }
+ }
+ thisRequest.pending += 1;
+ openmct.objects.get(domainObject.getId())
+ .then(function (obj) {
+ thisRequest.pending -= 1;
+ if (thisRequest !== currentRequest) {
+ return;
+ }
+ newFormatObject = obj;
+ composition = openmct.composition.get(obj);
+ composition.on('add', addChild);
+ composition.on('remove', removeChild);
+ composition.load();
+ unlisten = function () {
+ composition.off('add', addChild);
+ composition.off('remove', removeChild);
+ };
+ });
+ }
+
+ $scope.$watch('domainObject', onDomainObjectChange);
+
+ $scope.$on('plot:tickWidth', function ($e, width) {
+ var plotId = $e.targetScope.domainObject.getId();
+ if (!tickWidthMap.hasOwnProperty(plotId)) {
+ return;
+ }
+ tickWidthMap[plotId] = Math.max(width, tickWidthMap[plotId]);
+ var newTickWidth = _.max(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')
+ .finally(function () {
+ this.hideExportButtons = false;
+ }.bind(this));
+ };
+
+ StackedPlotController.prototype.exportPNG = function () {
+ this.hideExportButtons = true;
+ this.exportImageService.exportPNG(this.$element[0], 'stacked-plot.png')
+ .finally(function () {
+ this.hideExportButtons = false;
+ }.bind(this));
+ };
+
+ return StackedPlotController;
+});
diff --git a/src/plugins/plugins.js b/src/plugins/plugins.js
index efa857f263..0deb2f5497 100644
--- a/src/plugins/plugins.js
+++ b/src/plugins/plugins.js
@@ -30,7 +30,8 @@ define([
'../../platform/import-export/bundle',
'./summaryWidget/plugin',
'./URLIndicatorPlugin/URLIndicatorPlugin',
- './telemetryMean/plugin'
+ './telemetryMean/plugin',
+ './plot/plugin'
], function (
_,
UTCTimeSystem,
@@ -41,7 +42,8 @@ define([
ImportExport,
SummaryWidget,
URLIndicatorPlugin,
- TelemetryMean
+ TelemetryMean,
+ PlotPlugin
) {
var bundleMap = {
CouchDB: 'platform/persistence/couch',
@@ -126,6 +128,8 @@ define([
};
plugins.ExampleImagery = ExampleImagery;
+ plugins.Plot = PlotPlugin;
+
plugins.SummaryWidget = SummaryWidget;
plugins.TelemetryMean = TelemetryMean;
plugins.URLIndicatorPlugin = URLIndicatorPlugin;