Compare commits
	
		
			15 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 57abac96b2 | ||
|   | 204f6be9d0 | ||
|   | c988c6c43b | ||
|   | c143475f3e | ||
|   | b248f57daa | ||
|   | f4e8c5ecf6 | ||
|   | 11ef7c0ccd | ||
|   | 9c125e0454 | ||
|   | 439a654f6d | ||
|   | 9d32a3f1c3 | ||
|   | 705e25df8a | ||
|   | 787d6887dc | ||
|   | 1d516240a8 | ||
|   | 90ee0a309f | ||
|   | 72d05283da | 
| @@ -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,53 +20,70 @@ | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| .l-palette { | ||||
| 	$d: 16px; | ||||
| 	$colorsPerRow: 10; | ||||
| 	$m: 1; | ||||
|  | ||||
| 	box-sizing: border-box; | ||||
| 	padding: $interiorMargin !important; | ||||
| } | ||||
|  | ||||
| 	.l-palette-row { | ||||
| 		@include clearfix; | ||||
| 		line-height: $d; | ||||
| 		width: ($d * $colorsPerRow) + ($m * $colorsPerRow); | ||||
| .l-palette-row { | ||||
|     $d: 16px; | ||||
|     $m: 1; | ||||
|     $colorsPerRow: 10; | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|     line-height: $d; | ||||
|     width: ($d * $colorsPerRow) + ($m * $colorsPerRow); | ||||
|  | ||||
|         &.l-option-row { | ||||
|             margin-bottom: $interiorMargin; | ||||
|             .s-palette-item { | ||||
|                 border-color: $colorPaletteFg; | ||||
|     &.l-option-row { | ||||
|         margin-bottom: $interiorMargin; | ||||
|         .s-palette-item { | ||||
|             border-color: $colorPaletteFg; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .l-palette-item { | ||||
|         box-sizing: border-box; | ||||
|         display: block; | ||||
|         height: $d; width: $d; | ||||
|         min-width: $d; | ||||
|         line-height: $d * 0.9; | ||||
|         margin: 0 ($m * 1px) ($m * 1px) 0; | ||||
|         position: relative; | ||||
|         text-align: center; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .s-palette-item { | ||||
|     border: 1px solid transparent; | ||||
|     color: $colorPaletteFg; | ||||
|     text-shadow: $shdwPaletteFg; | ||||
|     @include trans-prop-nice-fade(0.25s); | ||||
|     &:hover { | ||||
|         @include trans-prop-nice-fade(0); | ||||
|         border-color: $colorPaletteSelected !important; | ||||
|     } | ||||
|     &.selected { | ||||
|         border-color: $colorPaletteSelected; | ||||
|         box-shadow: $shdwPaletteSelected; //Needed to see selection rect on light colored swatches | ||||
|     } | ||||
| } | ||||
|  | ||||
| .l-palette-item-label { | ||||
|     margin-left: $interiorMargin; | ||||
| } | ||||
|  | ||||
| .l-inline-palette { | ||||
|     .l-palette-row { | ||||
|         width: 100%; | ||||
|         .l-palette-item { | ||||
|             //@include display(flex); | ||||
|             @include flex(1 0 auto); | ||||
|             margin: 1px; | ||||
|             min-width: auto; | ||||
|             width: auto; | ||||
|             &:before { | ||||
|                 content: ''; | ||||
|                 padding-top: 75%; | ||||
|             } | ||||
|         } | ||||
|  | ||||
| 		.l-palette-item { | ||||
| 			box-sizing: border-box; | ||||
| 			display: block; | ||||
| 			float: left; | ||||
| 			height: $d; width: $d; | ||||
| 			line-height: $d * 0.9; | ||||
| 			margin: 0 ($m * 1px) ($m * 1px) 0; | ||||
|             position: relative; | ||||
| 			text-align: center; | ||||
| 		} | ||||
|  | ||||
| 		.s-palette-item { | ||||
|             border: 1px solid transparent; | ||||
|             color: $colorPaletteFg; | ||||
|             text-shadow: $shdwPaletteFg; | ||||
|             @include trans-prop-nice-fade(0.25s); | ||||
| 			&:hover { | ||||
| 				@include trans-prop-nice-fade(0); | ||||
| 				border-color: $colorPaletteSelected !important; | ||||
| 			} | ||||
|             &.selected { | ||||
|                 border-color: $colorPaletteSelected; | ||||
|                 box-shadow: $shdwPaletteSelected; //Needed to see selection rect on light colored swatches | ||||
|             } | ||||
| 		} | ||||
|  | ||||
| 		.l-palette-item-label { | ||||
| 			margin-left: $interiorMargin; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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; } | ||||
|         &: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,11 +57,16 @@ define([ | ||||
|                 return vm; | ||||
|             }, {byValue: {}, byString: {}}); | ||||
|             this.formatter.format = function (value) { | ||||
|                 return this.enumerations.byValue[value]; | ||||
|                 if (typeof value === "number") { | ||||
|                     return this.enumerations.byValue[value] || value; | ||||
|                 } | ||||
|                 return value; | ||||
|             }.bind(this); | ||||
|             this.formatter.parse = function (string) { | ||||
|                 if (typeof string === "string" && this.enumerations.hasOwnProperty(string)) { | ||||
|                     return this.enumerations.byString[string]; | ||||
|                 if (typeof string === "string") { | ||||
|                     if (this.enumerations.byString.hasOwnProperty(string)) { | ||||
|                         return this.enumerations.byString[string]; | ||||
|                     } | ||||
|                 } | ||||
|                 return Number(string); | ||||
|             }.bind(this); | ||||
|   | ||||
| @@ -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; | ||||
| }); | ||||
							
								
								
									
										215
									
								
								src/plugins/plot/res/templates/mct-plot.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								src/plugins/plot/res/templates/mct-plot.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | ||||
| <!-- | ||||
|  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-brackets" | ||||
|                         ng-click="plot.syncConductor()" | ||||
|                         title="Synchronize Time Conductor to plot bounds"> | ||||
|                     </a> | ||||
|  | ||||
|                     <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; | ||||
|  | ||||
| }); | ||||
							
								
								
									
										401
									
								
								src/plugins/plot/src/chart/MCTChartController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										401
									
								
								src/plugins/plot/src/chart/MCTChartController.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,401 @@ | ||||
| /***************************************************************************** | ||||
|  * 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); | ||||
|         window.chart = 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
		Reference in New Issue
	
	Block a user