mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 21:27:52 +00:00
new-plot import (#1557)
Merge of new plot * Introduces new Plot object and view * Removes Old Plot * Add LAD support and state type to generators * Removes Telemetry Panel Type * Telemetry API Updates * UTCFormat.parse: passthrough numbers * TelemetryAPI: default request arguments * TelemetryAPI: fix enum formatting * Markup and styling to support new plots
This commit is contained in:
parent
6145843e86
commit
5726fe6313
@ -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;
|
||||
};
|
||||
|
||||
|
80
example/generator/StateGeneratorProvider.js
Normal file
80
example/generator/StateGeneratorProvider.js
Normal file
@ -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;
|
||||
|
||||
});
|
@ -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) {
|
||||
|
@ -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());
|
||||
};
|
||||
|
||||
|
@ -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',
|
||||
|
@ -58,11 +58,7 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.w-mct-example {
|
||||
div {
|
||||
margin-bottom: $interiorMarginLg;
|
||||
}
|
||||
}
|
||||
.w-mct-example > div { margin-bottom: $interiorMarginLg; }
|
||||
|
||||
code,
|
||||
pre {
|
||||
|
@ -89,7 +89,8 @@ module.exports = function(config) {
|
||||
"dist/reports/coverage",
|
||||
check: {
|
||||
global: {
|
||||
lines: 80
|
||||
lines: 80,
|
||||
excludes: ['src/plugins/plot/**/*.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));
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,7 @@
|
||||
@import "fixed-position";
|
||||
@import "lists/tabular";
|
||||
@import "plots/plots-main";
|
||||
@import "plots/legend";
|
||||
@import "iframe";
|
||||
@import "views";
|
||||
@import "items/item";
|
||||
|
@ -5,6 +5,7 @@
|
||||
}
|
||||
|
||||
.l-view-section {
|
||||
//@include test(orange, 0.1);
|
||||
@include absPosDefault(0);
|
||||
h2 {
|
||||
color: #fff;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,15 +20,16 @@
|
||||
* 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;
|
||||
$d: 16px;
|
||||
$m: 1;
|
||||
$colorsPerRow: 10;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
line-height: $d;
|
||||
width: ($d * $colorsPerRow) + ($m * $colorsPerRow);
|
||||
|
||||
@ -42,13 +43,14 @@
|
||||
.l-palette-item {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
float: left;
|
||||
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;
|
||||
@ -68,5 +70,20 @@
|
||||
.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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
208
platform/commonUI/general/res/sass/plots/_legend.scss
Normal file
208
platform/commonUI/general/res/sass/plots/_legend.scss
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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; }
|
||||
&.no-children {
|
||||
&:before { display: none; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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')) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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[]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
@ -1,70 +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.
|
||||
-->
|
||||
<div ng-controller="PlotOptionsController" class="flex-elem grows l-inspector-part">
|
||||
<em class="t-inspector-part-header" title="Display properties for this object">Plot Options</em>
|
||||
<mct-form
|
||||
ng-model="configuration.plot.xAxis"
|
||||
structure="xAxisForm"
|
||||
name="xAxisFormState"
|
||||
class="flex-elem l-flex-row no-margin">
|
||||
</mct-form>
|
||||
<mct-form
|
||||
ng-model="configuration.plot.yAxis"
|
||||
structure="yAxisForm"
|
||||
name="yAxisFormState"
|
||||
class="flex-elem l-flex-row no-margin">
|
||||
</mct-form>
|
||||
<div class="form">
|
||||
<div class="section-header ng-binding ng-scope">
|
||||
Plot Series
|
||||
</div>
|
||||
<ul class="first flex-elem grows vscroll">
|
||||
<ul class="tree">
|
||||
<li ng-repeat="child in children">
|
||||
<span ng-controller="ToggleController as toggle">
|
||||
<span ng-controller="TreeNodeController as treeNode">
|
||||
<span class="tree-item menus-to-left">
|
||||
<span
|
||||
class='ui-symbol view-control flex-elem has-children'
|
||||
ng-class="{ expanded: toggle.isActive() }"
|
||||
ng-click="toggle.toggle(); treeNode.trackExpansion()">
|
||||
</span>
|
||||
<mct-representation
|
||||
class="rep-object-label"
|
||||
key="'label'"
|
||||
mct-object="child">
|
||||
</mct-representation>
|
||||
</span>
|
||||
</span>
|
||||
<mct-form
|
||||
ng-class="{hidden: !toggle.isActive()}"
|
||||
ng-model="configuration.plot.series[$index]"
|
||||
structure="plotSeriesForm"
|
||||
name="plotOptionsState"
|
||||
class="flex-elem l-flex-row">
|
||||
</mct-form>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
@ -1,165 +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.
|
||||
-->
|
||||
<span ng-controller="PlotController as plot"
|
||||
class="abs holder holder-plot has-control-bar">
|
||||
<div class="l-control-bar" ng-show="!plot.hideExportButtons">
|
||||
<span class="l-btn-set">
|
||||
<a class="s-button t-export labeled icon-download"
|
||||
ng-click="plot.exportPNG()"
|
||||
title="Export This View's Data as PNG">
|
||||
PNG
|
||||
</a>
|
||||
<a class="s-button t-export labeled"
|
||||
ng-click="plot.exportJPG()"
|
||||
title="Export This View's Data as JPG">
|
||||
JPG
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="l-view-section">
|
||||
<div class="gl-plot"
|
||||
ng-style="{ height: 100 / plot.getSubPlots().length + '%'}"
|
||||
ng-repeat="subplot in plot.getSubPlots()">
|
||||
<div class="gl-plot-legend">
|
||||
<span class='plot-legend-item'
|
||||
ng-repeat="telemetryObject in subplot.getTelemetryObjects()"
|
||||
ng-class="plot.getLegendClass(telemetryObject)">
|
||||
<span class='plot-color-swatch'
|
||||
ng-style="{ 'background-color': plot.getColor($index) }">
|
||||
</span>
|
||||
<span class='title-label'>{{telemetryObject.getModel().name}}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-axis-area gl-plot-y">
|
||||
<div class="gl-plot-label gl-plot-y-label">
|
||||
{{axes[1].active.name}}
|
||||
</div>
|
||||
<div ng-repeat="tick in subplot.getRangeTicks()"
|
||||
class="gl-plot-tick gl-plot-y-tick-label"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-y-options gl-plot-local-controls"
|
||||
ng-if="axes[1].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[1].active"
|
||||
ng-options="option.name for option in axes[1].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-wrapper-display-area-and-x-axis">
|
||||
<mct-include key="'time-of-interest'"
|
||||
class="l-toi-holder show-val"
|
||||
ng-if="toiPerc"
|
||||
ng-class="{ 'pinned': toiPinned, 'val-to-left': toiPerc > 80 }"
|
||||
ng-style="{'left': toiPerc + '%'}"></mct-include>
|
||||
|
||||
<div class="gl-plot-coords"
|
||||
ng-if="subplot.isHovering() && subplot.getHoverCoordinates()">
|
||||
{{subplot.getHoverCoordinates()}}
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-display-area"
|
||||
ng-mouseenter="subplot.isHovering(true);"
|
||||
ng-mouseleave="subplot.isHovering(false)"
|
||||
ng-class="{ loading: plot.isRequestPending() }">
|
||||
|
||||
<!-- Out-of-bounds data indicators -->
|
||||
<!-- ng-show is temporarily hard-coded in next element -->
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-up"></div>
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-dwn"></div>
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in subplot.getDomainTicks()"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%', height: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)">
|
||||
</div>
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in subplot.getRangeTicks()"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%', width: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getRangeTicks().length - 1)">
|
||||
</div>
|
||||
<mct-chart draw="subplot.getDrawingObject()"
|
||||
ng-if="subplot.getTelemetryObjects().length > 0"
|
||||
ng-mousemove="subplot.hover($event)"
|
||||
mct-drag="subplot.continueDrag($event)"
|
||||
mct-drag-down="subplot.startDrag($event)"
|
||||
mct-drag-up="subplot.endDrag($event); plot.update()">
|
||||
</mct-chart>
|
||||
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
||||
<div class="l-local-controls gl-plot-local-controls t-plot-display-controls"
|
||||
ng-if="$first">
|
||||
<a class="s-button icon-arrow-left"
|
||||
ng-click="plot.stepBackPanZoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Restore previous pan/zoom">
|
||||
</a>
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="plot.unzoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Reset pan/zoom">
|
||||
</a>
|
||||
<div class="menu-element s-menu-button menus-to-left {{plot.getMode().cssClass}}"
|
||||
ng-if="plot.getModeOptions().length > 1"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||
<span>{{plot.getMode().name}}</span>
|
||||
<div class="menu" ng-show="toggle.isActive()">
|
||||
<ul>
|
||||
<li ng-repeat="option in plot.getModeOptions()"
|
||||
ng-click="plot.setMode(option); toggle.setState(false)"
|
||||
class="{{option.cssClass}}">
|
||||
{{option.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="$last" class="gl-plot-axis-area gl-plot-x">
|
||||
<div ng-repeat="tick in subplot.getDomainTicks()"
|
||||
class="gl-plot-tick gl-plot-x-tick-label"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-label gl-plot-x-label">
|
||||
{{axes[0].active.name}}
|
||||
</div>
|
||||
<div class="gl-plot-x-options gl-plot-local-controls"
|
||||
ng-if="axes[0].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[0].active"
|
||||
ng-options="option.name for option in axes[0].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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 = "<canvas style='position: absolute; background: none; width: 100%; height: 100%;'></canvas>";
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
);
|
@ -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;
|
||||
|
||||
}
|
||||
);
|
@ -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;
|
||||
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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;
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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('<div />');
|
||||
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());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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]);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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]);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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([]);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -205,6 +205,9 @@ define(
|
||||
},
|
||||
getPointCount: function () {
|
||||
return telemetry.length;
|
||||
},
|
||||
getData: function () {
|
||||
return telemetry;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -148,7 +148,9 @@ define([
|
||||
var limitEvaluator = oldObject.getCapability("limit");
|
||||
|
||||
if (!limitEvaluator) {
|
||||
return;
|
||||
return {
|
||||
evaluate: function () {}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,12 +57,17 @@ 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)) {
|
||||
if (typeof string === "string") {
|
||||
if (this.enumerations.byString.hasOwnProperty(string)) {
|
||||
return this.enumerations.byString[string];
|
||||
}
|
||||
}
|
||||
return Number(string);
|
||||
}.bind(this);
|
||||
}
|
||||
|
@ -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',
|
||||
|
250
src/plugins/plot/plugin.js
Normal file
250
src/plugins/plot/plugin.js
Normal file
@ -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;
|
||||
});
|
210
src/plugins/plot/res/templates/mct-plot.html
Normal file
210
src/plugins/plot/res/templates/mct-plot.html
Normal file
@ -0,0 +1,210 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<div class="gl-plot plot-legend-{{legend.get('position')}} {{legend.get('expanded')? 'plot-legend-expanded' : 'plot-legend-collapsed'}}">
|
||||
<div class="gl-plot-legend flex-elem l-flex-row"
|
||||
ng-class="{ 'hover-on-plot': !!highlights.length }"
|
||||
ng-show="legend.get('position') !== 'hidden'">
|
||||
<span class="view-control flex-elem"
|
||||
ng-class="{ expanded: legend.get('expanded') }"
|
||||
ng-click="legend.set('expanded', !legend.get('expanded'));">
|
||||
</span>
|
||||
|
||||
<!-- COLLAPSED PLOT LEGEND -->
|
||||
<div class="plot-wrapper-collapsed-legend">
|
||||
<div class="plot-legend-item"
|
||||
ng-repeat="series in series track by $index">
|
||||
<div class="plot-series-swatch-and-name">
|
||||
<span class="plot-series-color-swatch"
|
||||
ng-style="{ 'background-color': series.get('color').asHexString() }">
|
||||
</span>
|
||||
<span class="plot-series-name">{{ series.get('name') }}</span>
|
||||
</div>
|
||||
<div class="plot-series-value hover-value-enabled value-to-display-{{ legend.get('valueToShowWhenCollapsed') }} {{ series.closest._limit.cssClass }}"
|
||||
ng-class="{ 'cursor-hover': (legend.get('valueToShowWhenCollapsed').indexOf('nearest') != -1) }"
|
||||
ng-show="!!highlights.length && legend.get('valueToShowWhenCollapsed') !== 'none'">
|
||||
{{ legend.get('valueToShowWhenCollapsed') === 'nearestValue' ?
|
||||
series.formatY(series.closest) :
|
||||
legend.get('valueToShowWhenCollapsed') === 'nearestTimestamp' ?
|
||||
series.closest && series.formatX(series.closest) :
|
||||
series.formatY(series.get('stats')[legend.get('valueToShowWhenCollapsed') + 'Point']);
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- EXPANDED PLOT LEGEND -->
|
||||
<div class="plot-wrapper-expanded-legend flex-elem grows">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th ng-if="legend.get('showTimestampWhenExpanded')">
|
||||
Timestamp
|
||||
</th>
|
||||
<th ng-if="legend.get('showValueWhenExpanded')">
|
||||
Value
|
||||
</th>
|
||||
<th ng-if="legend.get('showMinimumWhenExpanded')"
|
||||
class="mobile-hide">
|
||||
Min
|
||||
</th>
|
||||
<th ng-if="legend.get('showMaximumWhenExpanded')"
|
||||
class="mobile-hide">
|
||||
Max
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="series in series" class="plot-legend-item">
|
||||
<td class="plot-series-swatch-and-name">
|
||||
<span class="plot-series-color-swatch"
|
||||
ng-style="{ 'background-color': series.get('color').asHexString() }">
|
||||
</span>
|
||||
<span class="plot-series-name">{{ series.get('name') }}</span>
|
||||
</td>
|
||||
|
||||
<td ng-if="legend.get('showTimestampWhenExpanded')">
|
||||
<span class="plot-series-value cursor-hover hover-value-enabled">
|
||||
{{ series.closest && series.formatX(series.closest) }}
|
||||
</span>
|
||||
</td>
|
||||
<td ng-if="legend.get('showValueWhenExpanded')">
|
||||
<span class="plot-series-value cursor-hover hover-value-enabled"
|
||||
ng-class="series.closest._limit.cssClass">
|
||||
{{ series.formatY(series.closest) }}
|
||||
</span>
|
||||
</td>
|
||||
<td ng-if="legend.get('showMinimumWhenExpanded')"
|
||||
class="mobile-hide">
|
||||
<span class="plot-series-value">
|
||||
{{ series.formatY(series.get('stats').minPoint) }}
|
||||
</span>
|
||||
</td>
|
||||
<td ng-if="legend.get('showMaximumWhenExpanded')"
|
||||
class="mobile-hide">
|
||||
<span class="plot-series-value">
|
||||
{{ series.formatY(series.get('stats').maxPoint) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="plot-wrapper-axis-and-display-area flex-elem grows">
|
||||
<div class="gl-plot-axis-area gl-plot-y"
|
||||
ng-style="{
|
||||
width: (tickWidth + 30) + 'px'
|
||||
}">
|
||||
|
||||
<div class="gl-plot-label gl-plot-y-label">
|
||||
{{ yAxis.get('label') }}
|
||||
</div>
|
||||
|
||||
<mct-ticks axis="yAxis">
|
||||
<div ng-repeat="tick in ticks track by tick.text"
|
||||
class="gl-plot-tick gl-plot-y-tick-label"
|
||||
ng-style="{ top: (100 * (max - tick.value) / interval) + '%' }"
|
||||
title="{{:: tick.fullText || tick.text }}"
|
||||
style="margin-top: -0.50em; direction: ltr;">
|
||||
<span>{{:: tick.text}}</span>
|
||||
</div>
|
||||
</mct-ticks>
|
||||
</div>
|
||||
<div class="gl-plot-wrapper-display-area-and-x-axis"
|
||||
ng-style="{
|
||||
left: (tickWidth + 30) + 'px'
|
||||
}">
|
||||
<span class="t-object-alert t-alert-unsynced" title="This plot is not currently displaying the latest data. Reset Pan/zoom to return to view latest data."></span>
|
||||
<div class="gl-plot-display-area">
|
||||
<mct-ticks axis="xAxis">
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in ticks track by tick.value"
|
||||
ng-style="{
|
||||
right: (100 * (max - tick.value) / interval) + '%',
|
||||
height: '100%'
|
||||
}">
|
||||
</div>
|
||||
</mct-ticks>
|
||||
|
||||
|
||||
<mct-ticks axis="yAxis">
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in ticks track by tick.value"
|
||||
ng-style="{ bottom: (100 * (tick.value - min) / interval) + '%', width: '100%' }">
|
||||
</div>
|
||||
</mct-ticks>
|
||||
|
||||
|
||||
<mct-chart config="config"
|
||||
series="series"
|
||||
rectangles="rectangles"
|
||||
highlights="highlights"
|
||||
the-x-axis="xAxis"
|
||||
the-y-axis="yAxis">
|
||||
</mct-chart>
|
||||
|
||||
<div class="l-local-controls gl-plot-local-controls"
|
||||
ng-show="plotHistory.length"
|
||||
style="position: absolute; top: 8px; right: 8px;">
|
||||
|
||||
<a class="s-button icon-arrow-left"
|
||||
ng-click="plot.back()"
|
||||
title="Restore previous pan/zoom">
|
||||
</a>
|
||||
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="plot.clear()"
|
||||
title="Reset pan/zoom">
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<span class="t-wait-spinner loading" ng-show="plot.isRequestPending()">
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-axis-area gl-plot-x"
|
||||
ng-style="{
|
||||
left: (tickWidth - 30) + 'px'
|
||||
}">
|
||||
|
||||
<mct-ticks axis="xAxis">
|
||||
<div ng-repeat="tick in ticks track by tick.text"
|
||||
class="gl-plot-tick gl-plot-x-tick-label"
|
||||
ng-style="{
|
||||
left: (100 * (tick.value - min) / interval) + '%'
|
||||
}"
|
||||
ng-title=":: tick.fullText || tick.text">
|
||||
{{:: tick.text | reverse}}
|
||||
</div>
|
||||
</mct-ticks>
|
||||
|
||||
<div class="gl-plot-label gl-plot-x-label">
|
||||
{{ xAxis.get('label') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
130
src/plugins/plot/res/templates/plot-options-browse.html
Normal file
130
src/plugins/plot/res/templates/plot-options-browse.html
Normal file
@ -0,0 +1,130 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<div ng-controller="PlotOptionsController"
|
||||
class="flex-elem grows l-inspector-part">
|
||||
<ul class="flex-elem grows l-inspector-part"><li>
|
||||
<em class="t-inspector-part-header">Plot Series</em>
|
||||
<ul class="first flex-elem grows vscroll">
|
||||
<ul class="tree">
|
||||
<li ng-repeat="series in config.series.models">
|
||||
<span class="tree-item menus-to-left">
|
||||
<span
|
||||
class='ui-symbol view-control flex-elem'
|
||||
ng-class="{ expanded: series.expanded }"
|
||||
ng-click="series.expanded = !series.expanded">
|
||||
</span>
|
||||
<mct-representation
|
||||
class="rep-object-label"
|
||||
key="'label'"
|
||||
mct-object="series.oldObject">
|
||||
</mct-representation>
|
||||
</span>
|
||||
<div class="l-flex-col inspector-config" ng-show="series.expanded">
|
||||
<div class="inspector-properties">
|
||||
<div class="label">Line Style</div>
|
||||
<div class="value">{{ {
|
||||
'none': 'None',
|
||||
'linear': 'Linear interpolation',
|
||||
'stepAfter': 'Step After'
|
||||
}[series.get('interpolate')] }}</div>
|
||||
</div>
|
||||
<div class="inspector-properties">
|
||||
<div class="label">Markers</div>
|
||||
<div class="value">
|
||||
{{series.get('markers') ? "On, " + series.get('markerSize') + "px" : "Off"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inspector-properties">
|
||||
<div class="label">Color</div>
|
||||
<div class="value">
|
||||
<span class="color-swatch"
|
||||
ng-style="{
|
||||
'background': series.get('color').asHexString(),
|
||||
'display': 'inline-block',
|
||||
'border': '1px solid rgba(255, 255, 255, 0.2)',
|
||||
'height': '10px',
|
||||
'width': '10px',
|
||||
'vertical-align': 'middle',
|
||||
'margin-left': '3px',
|
||||
'margin-top': -'2px'
|
||||
}">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<em class="t-inspector-part-header">Y Axis</em>
|
||||
<div class="inspector-properties first">
|
||||
<div class="label">Label</div>
|
||||
<div class="value">{{ config.yAxis.get('label') }}</div>
|
||||
</div>
|
||||
<div class="inspector-properties">
|
||||
<div class="label">Autoscale</div>
|
||||
<div class="value">
|
||||
{{ config.yAxis.get('autoscale') ? "On" : "Off" }}
|
||||
{{ config.yAxis.get('autoscale') ? (config.yAxis.get('autoscalePadding') * 100) + "%" : ""}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="inspector-properties"
|
||||
ng-if="!form.yAxis.autoscale">
|
||||
<div class="label">Min</div>
|
||||
<div class="value">{{ config.yAxis.get('range').min }}</div>
|
||||
</div>
|
||||
<div class="inspector-properties"
|
||||
ng-if="!form.yAxis.autoscale">
|
||||
<div class="label">Max</div>
|
||||
<div class="value">{{ config.yAxis.get('range').max }}</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<em class="t-inspector-part-header">Legend</em>
|
||||
<div class="inspector-properties first">
|
||||
<div class="label">Position</div>
|
||||
<div class="value">{{ config.legend.get('position') }}</div>
|
||||
</div>
|
||||
<div class="inspector-properties">
|
||||
<div class="label">Expand by Default</div>
|
||||
<div class="value">{{ config.legend.get('expandByDefault') ? "Yes" : "No" }}</div>
|
||||
</div>
|
||||
<div class="inspector-properties">
|
||||
<div class="label">Show when collapsed:</div>
|
||||
<div class="value">{{
|
||||
config.legend.get('valueToShowWhenCollapsed').replace('nearest', '')
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="inspector-properties">
|
||||
<div class="label">Show when expanded:</div>
|
||||
<div class="value comma-list">
|
||||
<span ng-if="config.legend.get('showTimestampWhenExpanded')">Timestamp</span>
|
||||
<span ng-if="config.legend.get('showValueWhenExpanded')">Value</span>
|
||||
<span ng-if="config.legend.get('showMinimumWhenExpanded')">Min</span>
|
||||
<span ng-if="config.legend.get('showMaximumWhenExpanded')">Max</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
229
src/plugins/plot/res/templates/plot-options-edit.html
Normal file
229
src/plugins/plot/res/templates/plot-options-edit.html
Normal file
@ -0,0 +1,229 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<div ng-controller="PlotOptionsController"
|
||||
class="flex-elem grows l-inspector-part">
|
||||
<em class="t-inspector-part-header"
|
||||
title="Display properties for this object">
|
||||
Series Options
|
||||
</em>
|
||||
<ul class="first flex-elem grows vscroll">
|
||||
<ul class="tree">
|
||||
<li ng-repeat="series in config.series.models">
|
||||
<span class="tree-item menus-to-left">
|
||||
<span
|
||||
class='ui-symbol view-control flex-elem'
|
||||
ng-class="{ expanded: series.expanded }"
|
||||
ng-click="series.expanded = !series.expanded">
|
||||
</span>
|
||||
<mct-representation
|
||||
class="rep-object-label"
|
||||
key="'label'"
|
||||
mct-object="series.oldObject">
|
||||
</mct-representation>
|
||||
</span>
|
||||
<div class="inspector-config" ng-show="series.expanded">
|
||||
<ul>
|
||||
<li>
|
||||
<label>Value:</label>
|
||||
<span class="control">
|
||||
<div class="select">
|
||||
<select ng-model="form.series[$index].yKey">
|
||||
<option ng-repeat="option in form.series[$index].yAxisOptions"
|
||||
value="{{option.value}}"
|
||||
ng-selected="option.value == form.series[$index].yKey">
|
||||
{{option.name}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>Line Style:</label>
|
||||
<span class="control">
|
||||
<div class="select">
|
||||
<select ng-model="form.series[$index].interpolate">
|
||||
<option value="none">None</option>
|
||||
<option value="linear">Linear interpolate</option>
|
||||
<option value="stepAfter">Step After</option>
|
||||
</select>
|
||||
</div>
|
||||
</span>
|
||||
</li>
|
||||
<li class="controls-first">
|
||||
<label>Show Markers</label>
|
||||
<span class="control"><input type="checkbox" ng-model="form.series[$index].markers"/></span>
|
||||
</li>
|
||||
<li class="controls-first">
|
||||
<label>Show Alarm Markers</label>
|
||||
<span class="control"><input type="checkbox" ng-model="form.series[$index].alarmMarkers"/></span>
|
||||
</li>
|
||||
<li ng-show="form.series[$index].markers || form.series[$index].alarmMarkers">
|
||||
<label>Marker Size:</label>
|
||||
<span class="control"><input class="sm" type="text" ng-model="form.series[$index].markerSize"/></span>
|
||||
</li>
|
||||
<ul ng-controller="ClickAwayController as toggle" ng-show="form.series[$index].interpolate !== 'none' || form.series[$index].markers">
|
||||
<li>
|
||||
<label>Color:</label>
|
||||
<span class="control">
|
||||
<div class="s-menu-button" ng-click="toggle.toggle()">
|
||||
<span class="color-swatch" ng-style="{ background: series.get('color').asHexString() }">
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</li>
|
||||
<li class="connects-to-previous l-inline-palette" ng-show="toggle.isActive()">
|
||||
<div class="l-palette-row" ng-repeat="group in config.series.palette.groups()">
|
||||
<div class="l-palette-item s-palette-item"
|
||||
ng-repeat="color in group"
|
||||
xng-class="{ 'icon-check': series.get('color') === color }"
|
||||
ng-style="{ background: color.asHexString() }"
|
||||
ng-click="setColor(series, color, config.series.models.indexOf(series))">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
<form class="inspector-config"
|
||||
ng-show="!!config.series.models.length">
|
||||
<em class="t-inspector-part-header"
|
||||
title="Options for plot axes display">
|
||||
Y Axis
|
||||
</em>
|
||||
<ul>
|
||||
<li>
|
||||
<label>Label:</label>
|
||||
<input class="control" type="text" ng-model="form.yAxis.label"/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul ng-show="!(form.yAxis.key == 'enum')">
|
||||
<li class="section-header">Scaling</li>
|
||||
<li class="controls-first">
|
||||
<label>Autoscale</label>
|
||||
<span class="control">
|
||||
<input type="checkbox" ng-model="form.yAxis.autoscale"/>
|
||||
</span>
|
||||
</li>
|
||||
<li class="form-error"
|
||||
ng-show="!(form.yAxis.key == 'enum') && !form.yAxis.autoscale && validation['form.yAxis.range']">
|
||||
{{ validation['form.yAxis.range'] }}
|
||||
</li>
|
||||
<li ng-show="!form.yAxis.autoscale">
|
||||
<label>Minimum:</label>
|
||||
<span class="control">
|
||||
<input class="sm" type="text" ng-model="form.yAxis.range.min"/>
|
||||
</span>
|
||||
</li>
|
||||
<li ng-show="!form.yAxis.autoscale">
|
||||
<label>Maximum:</label>
|
||||
<span class="control">
|
||||
<input class="sm" type="text" ng-model="form.yAxis.range.max"/>
|
||||
</span>
|
||||
</li>
|
||||
<li ng-show="form.yAxis.autoscale">
|
||||
<label>Padding:</label>
|
||||
<span class="control">
|
||||
<input class="sm" type="text" ng-model="form.yAxis.autoscalePadding"/>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<em class="t-inspector-part-header"
|
||||
title="Options for legend display">
|
||||
Legend
|
||||
</em>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label>Position:</label>
|
||||
<span class="control">
|
||||
<div class="select">
|
||||
<select ng-model="form.legend.position">
|
||||
<option value="hidden">Hidden</option>
|
||||
<option value="top">Top</option>
|
||||
<option value="right">Right</option>
|
||||
<option value="bottom">Bottom</option>
|
||||
<option value="left">Left</option>
|
||||
</select>
|
||||
</div>
|
||||
</span>
|
||||
</li>
|
||||
<li class="controls-first">
|
||||
<label>Expand by default</label>
|
||||
<span class="control">
|
||||
<input type="checkbox" ng-model="form.legend.expandByDefault"/>
|
||||
</span>
|
||||
</li>
|
||||
<li class="controls-under">
|
||||
<label>Show when collapsed:</label>
|
||||
<span class="control">
|
||||
<div class="select">
|
||||
<select ng-model="form.legend.valueToShowWhenCollapsed">
|
||||
<option value="none">None</option>
|
||||
<option value="nearestTimestamp">Nearest Timestamp</option>
|
||||
<option value="nearestValue">Nearest Value</option>
|
||||
<option value="min">Minimum</option>
|
||||
<option value="max">Maximum</option>
|
||||
</select>
|
||||
</div>
|
||||
</span>
|
||||
</li>
|
||||
<li class="controls-under">
|
||||
<ul>
|
||||
<li><label>Show when expanded:</label></li>
|
||||
<li class="controls-first">
|
||||
<label>Nearest Timestamp</label>
|
||||
<span class="control">
|
||||
<input type="checkbox"
|
||||
ng-model="form.legend.showTimestampWhenExpanded"/>
|
||||
</span>
|
||||
</li>
|
||||
<li class="controls-first">
|
||||
<label>Nearest Value</label>
|
||||
<span class="control">
|
||||
<input type="checkbox"
|
||||
ng-model="form.legend.showValueWhenExpanded"/>
|
||||
</span>
|
||||
</li>
|
||||
<li class="controls-first">
|
||||
<label>Minimum</label>
|
||||
<span class="control">
|
||||
<input type="checkbox"
|
||||
ng-model="form.legend.showMinimumWhenExpanded"/>
|
||||
</span>
|
||||
</li>
|
||||
<li class="controls-first">
|
||||
<label>Maximum</label>
|
||||
<span class="control">
|
||||
<input type="checkbox"
|
||||
ng-model="form.legend.showMaximumWhenExpanded"/>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
31
src/plugins/plot/res/templates/plot-options.html
Normal file
31
src/plugins/plot/res/templates/plot-options.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<div ng-if="domainObject.getCapability('editor').inEditContext()">
|
||||
<mct-representation key="'plot-options-edit'"
|
||||
mct-object="domainObject">
|
||||
</mct-representation>
|
||||
</div>
|
||||
<div ng-if="!domainObject.getCapability('editor').inEditContext()">
|
||||
<mct-representation key="'plot-options-browse'"
|
||||
mct-object="domainObject">
|
||||
</mct-representation>
|
||||
</div>
|
50
src/plugins/plot/res/templates/plot.html
Normal file
50
src/plugins/plot/res/templates/plot.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<span ng-controller="PlotController as controller"
|
||||
class="abs holder holder-plot has-control-bar"
|
||||
ng-class="{
|
||||
'loading': !!pending
|
||||
}"
|
||||
>
|
||||
<div class="l-control-bar" ng-show="!controller.hideExportButtons">
|
||||
<span class="l-btn-set">
|
||||
<a class="s-button t-export labeled icon-download"
|
||||
ng-click="controller.exportPNG()"
|
||||
title="Export This View's Data as PNG">
|
||||
PNG
|
||||
</a>
|
||||
<a class="s-button t-export labeled"
|
||||
ng-click="controller.exportJPG()"
|
||||
title="Export This View's Data as JPG">
|
||||
JPG
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="l-view-section">
|
||||
<mct-plot config="controller.config"
|
||||
series="series"
|
||||
the-y-axis="yAxis"
|
||||
the-x-axis="xAxis">
|
||||
</mct-plot>
|
||||
</div>
|
||||
</span>
|
53
src/plugins/plot/res/templates/stacked-plot.html
Normal file
53
src/plugins/plot/res/templates/stacked-plot.html
Normal file
@ -0,0 +1,53 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<span ng-controller="StackedPlotController as stackedPlot"
|
||||
class="abs holder holder-plot has-control-bar t-plot-stacked"
|
||||
ng-class="{
|
||||
'loading': !!currentRequest.pending
|
||||
}">
|
||||
|
||||
<div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons">
|
||||
<span class="l-btn-set">
|
||||
<a class="s-button t-export labeled icon-download"
|
||||
ng-click="stackedPlot.exportPNG()"
|
||||
title="Export This View's Data as PNG">
|
||||
PNG
|
||||
</a>
|
||||
<a class="s-button t-export labeled"
|
||||
ng-click="stackedPlot.exportJPG()"
|
||||
title="Export This View's Data as JPG">
|
||||
JPG
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="l-view-section">
|
||||
<div class="gl-plot child-frame"
|
||||
ng-repeat="telemetryObject in telemetryObjects"
|
||||
ng-class="{
|
||||
's-status-timeconductor-unsynced': telemetryObject
|
||||
.getCapability('status')
|
||||
.get('timeconductor-unsynced')
|
||||
}">
|
||||
<mct-overlay-plot domain-object="telemetryObject"></mct-overlay-plot>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
@ -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);
|
||||
}
|
||||
|
77
src/plugins/plot/src/chart/MCTChartAlarmPointSet.js
Normal file
77
src/plugins/plot/src/chart/MCTChartAlarmPointSet.js
Normal file
@ -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;
|
||||
|
||||
});
|
400
src/plugins/plot/src/chart/MCTChartController.js
Normal file
400
src/plugins/plot/src/chart/MCTChartController.js
Normal file
@ -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;
|
||||
});
|
67
src/plugins/plot/src/chart/MCTChartDirective.js
Normal file
67
src/plugins/plot/src/chart/MCTChartDirective.js
Normal file
@ -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 = "<canvas style='position: absolute; background: none; width: 100%; height: 100%;'></canvas>";
|
||||
TEMPLATE += TEMPLATE;
|
||||
|
||||
/**
|
||||
* MCTChart draws charts utilizing a drawAPI.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function MCTChart() {
|
||||
return {
|
||||
restrict: "E",
|
||||
template: TEMPLATE,
|
||||
link: function ($scope, $element, attrs, ctrl) {
|
||||
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;
|
||||
});
|
40
src/plugins/plot/src/chart/MCTChartLineLinear.js
Normal file
40
src/plugins/plot/src/chart/MCTChartLineLinear.js
Normal file
@ -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;
|
||||
|
||||
});
|
||||
|
78
src/plugins/plot/src/chart/MCTChartLineStepAfter.js
Normal file
78
src/plugins/plot/src/chart/MCTChartLineStepAfter.js
Normal file
@ -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;
|
||||
|
||||
});
|
||||
|
40
src/plugins/plot/src/chart/MCTChartPointSet.js
Normal file
40
src/plugins/plot/src/chart/MCTChartPointSet.js
Normal file
@ -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;
|
||||
|
||||
});
|
||||
|
162
src/plugins/plot/src/chart/MCTChartSeriesElement.js
Normal file
162
src/plugins/plot/src/chart/MCTChartSeriesElement.js
Normal file
@ -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;
|
||||
});
|
133
src/plugins/plot/src/configuration/Collection.js
Normal file
133
src/plugins/plot/src/configuration/Collection.js
Normal file
@ -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;
|
||||
});
|
61
src/plugins/plot/src/configuration/LegendModel.js
Normal file
61
src/plugins/plot/src/configuration/LegendModel.js
Normal file
@ -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;
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user