mirror of
https://github.com/nasa/openmct.git
synced 2025-04-13 22:23:13 +00:00
[Plot] Bring in scripts from sandbox branch
Bring in scripts for plotting from the sandbox branch to begin transitioning/integrating plot view. WTD-533.
This commit is contained in:
parent
55c2d15cdc
commit
b556b5e4f2
27
platform/features/plot/bundle.json
Normal file
27
platform/features/plot/bundle.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "Plot view for telemetry",
|
||||
"extensions": {
|
||||
"views": [
|
||||
{
|
||||
"name": "Plot",
|
||||
"templateUrl": "templates/plot.html",
|
||||
"needs": [ "telemetry" ],
|
||||
"delegation": true
|
||||
}
|
||||
],
|
||||
"directives": [
|
||||
{
|
||||
"key": "mctChart",
|
||||
"implementation": "MCTChart.js",
|
||||
"depends": [ "$interval" ]
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "PlotController",
|
||||
"implementation": "PlotController.js",
|
||||
"depends": [ "$scope" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
6
platform/features/plot/lib/moment.min.js
vendored
Normal file
6
platform/features/plot/lib/moment.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
121
platform/features/plot/res/templates/plot.html
Normal file
121
platform/features/plot/res/templates/plot.html
Normal file
@ -0,0 +1,121 @@
|
||||
<div class="gl-plot"
|
||||
ng-controller="TelemetryController as telemetry">
|
||||
<span ng-controller="PlotController as plot"
|
||||
ng-mouseleave="representation.showControls = false">
|
||||
|
||||
<div class="gl-plot-legend">
|
||||
<span class='plot-legend-item'
|
||||
ng-repeat="telemetryObject in telemetry.getTelemetryObjects() track by $index">
|
||||
<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-coords"
|
||||
ng-if="representation.showControls">
|
||||
{{plot.getHoverCoordinates().join(', ')}}
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-axis-area gl-plot-y">
|
||||
|
||||
<div class="gl-plot-label gl-plot-y-label"
|
||||
ng-show="!representation.showControls">
|
||||
{{axes[1].active.name}}
|
||||
</div>
|
||||
|
||||
<div ng-repeat="tick in axes[1].ticks"
|
||||
class="gl-plot-tick gl-plot-y-tick-label"
|
||||
ng-style="{ bottom: (100 * $index / (axes[1].ticks.length - 1)) + '%' }">
|
||||
{{tick.label}}
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-y-options gl-plot-local-controls"
|
||||
ng-show="representation.showControls"
|
||||
ng-if="axes[1].options.length > 0">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell select"
|
||||
ng-model="axes[1].active"
|
||||
ng-options="option.name for option in axes[1].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-display-area"
|
||||
ng-mouseenter="representation.showControls = true">
|
||||
|
||||
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in axes[0].ticks"
|
||||
ng-style="{ left: (100 * $index / (axes[0].ticks.length - 1)) + '%', height: '100%' }"
|
||||
ng-show="$index > 0 && $index < (axes[0].ticks.length - 1)">
|
||||
</div>
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in axes[1].ticks"
|
||||
ng-style="{ bottom: (100 * $index / (axes[1].ticks.length - 1)) + '%', width: '100%' }"
|
||||
ng-show="$index > 0 && $index < (axes[1].ticks.length - 1)">
|
||||
</div>
|
||||
|
||||
<mct-chart draw="draw"
|
||||
ng-mousemove="plot.hover($event)"
|
||||
ng-mousedown="plot.startMarquee($event)"
|
||||
ng-mouseup="plot.endMarquee($event)">
|
||||
</mct-chart>
|
||||
|
||||
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
||||
<div class="gl-plot-local-controls"
|
||||
style="position: absolute; top: 8px; right: 8px;">
|
||||
|
||||
<a href=""
|
||||
class="t-btn l-btn s-btn s-icon-btn s-very-subtle"
|
||||
ng-click="plot.stepBackPanZoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Restore previous pan/zoom">
|
||||
<span class="ui-symbol icon">B</span>
|
||||
</a>
|
||||
|
||||
<a href=""
|
||||
class="t-btn l-btn s-btn s-icon-btn s-very-subtle"
|
||||
ng-click="plot.unzoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Reset pan/zoom">
|
||||
<span class="ui-symbol icon">R</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<span class="t-wait-spinner loading" ng-show="telemetry.isRequestPending()">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-axis-area gl-plot-x">
|
||||
|
||||
<div ng-repeat="tick in axes[0].ticks"
|
||||
class="gl-plot-tick gl-plot-x-tick-label"
|
||||
ng-show="$index > 0 && $index < (axes[0].ticks.length - 1)"
|
||||
ng-style="{ left: (100 * $index / (axes[0].ticks.length - 1)) + '%' }">
|
||||
{{tick.label}}
|
||||
</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-show="representation.showControls"
|
||||
ng-if="axes[0].options.length > 0">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell select"
|
||||
ng-model="axes[0].active"
|
||||
ng-options="option.name for option in axes[0].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
99
platform/features/plot/src/GLPlot.js
Normal file
99
platform/features/plot/src/GLPlot.js
Normal file
@ -0,0 +1,99 @@
|
||||
/*global define,Promise,Float32Array*/
|
||||
|
||||
/**
|
||||
* Module defining GLPlot. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
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');
|
||||
|
||||
function GLPlot(canvas) {
|
||||
var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"),
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
program,
|
||||
aVertexPosition,
|
||||
uColor,
|
||||
uDimensions,
|
||||
uOrigin,
|
||||
buffer;
|
||||
|
||||
if (!gl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
program = gl.createProgram();
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
gl.useProgram(program);
|
||||
|
||||
aVertexPosition = gl.getAttribLocation(program, "aVertexPosition");
|
||||
gl.enableVertexAttribArray(aVertexPosition);
|
||||
|
||||
uColor = gl.getUniformLocation(program, "uColor");
|
||||
uDimensions = gl.getUniformLocation(program, "uDimensions");
|
||||
uOrigin = gl.getUniformLocation(program, "uOrigin");
|
||||
|
||||
buffer = gl.createBuffer();
|
||||
|
||||
gl.lineWidth(2.0);
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
function doDraw(drawType, buf, color, points) {
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, buf, gl.DYNAMIC_DRAW);
|
||||
gl.vertexAttribPointer(aVertexPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform4fv(uColor, color);
|
||||
gl.drawArrays(drawType, 0, points);
|
||||
}
|
||||
|
||||
return {
|
||||
clear: function () {
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT);
|
||||
},
|
||||
setDimensions: function (dimensions, origin) {
|
||||
gl.uniform2fv(uDimensions, dimensions);
|
||||
gl.uniform2fv(uOrigin, origin);
|
||||
},
|
||||
drawLine: function (buf, color, points) {
|
||||
doDraw(gl.LINE_STRIP, buf, color, points);
|
||||
},
|
||||
drawSquare: function (min, max, color) {
|
||||
doDraw(gl.TRIANGLE_FAN, new Float32Array(
|
||||
min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])
|
||||
), color, 4);
|
||||
},
|
||||
gl: gl
|
||||
};
|
||||
}
|
||||
return GLPlot;
|
||||
}
|
||||
);
|
92
platform/features/plot/src/GLPlotPreparer.js
Normal file
92
platform/features/plot/src/GLPlotPreparer.js
Normal file
@ -0,0 +1,92 @@
|
||||
/*global define,Float32Array*/
|
||||
|
||||
/**
|
||||
* Prepares data to be rendered in a GL Plot. Handles
|
||||
* the conversion from data API to displayable buffers.
|
||||
*/
|
||||
define(
|
||||
function () {
|
||||
'use strict';
|
||||
|
||||
function identity(x) { return x; }
|
||||
|
||||
function GLPlotPreparer(datas, domain, range) {
|
||||
var index,
|
||||
vertices = [],
|
||||
max = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
||||
min = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
||||
x,
|
||||
y,
|
||||
xLabels = {},
|
||||
yLabels = {},
|
||||
yUnits = {},
|
||||
domainOffset = Number.POSITIVE_INFINITY,
|
||||
buffers;
|
||||
|
||||
datas = datas || [];
|
||||
|
||||
datas.filter(identity).forEach(function (data) {
|
||||
domainOffset = Math.min(data.getDomainValue(0, domain), domainOffset);
|
||||
});
|
||||
|
||||
datas.forEach(function (data, i) {
|
||||
if (!data) {
|
||||
return; // skip null data
|
||||
}
|
||||
|
||||
vertices.push([]);
|
||||
for (index = 0; index < data.getPointCount(); index = index + 1) {
|
||||
x = data.getDomainValue(index, domain) - domainOffset;
|
||||
y = data.getRangeValue(index, range);
|
||||
vertices[i].push(x);
|
||||
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 (data.getDomainLabel) {
|
||||
xLabels[data.getDomainLabel(domain)] = true;
|
||||
}
|
||||
|
||||
if (data.getRangeLabel) {
|
||||
yLabels[data.getRangeLabel(range)] = true;
|
||||
}
|
||||
|
||||
if (data.getRangeUnits) {
|
||||
yUnits[data.getRangeUnits(range)] = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (max[1] === min[1]) {
|
||||
max[1] = max[1] + 1.0;
|
||||
min[1] = min[1] - 1.0;
|
||||
}
|
||||
|
||||
xLabels = Object.keys(xLabels).sort();
|
||||
yLabels = Object.keys(yLabels).sort();
|
||||
yUnits = Object.keys(yUnits).sort();
|
||||
|
||||
buffers = vertices.map(function (v) { return new Float32Array(v); });
|
||||
|
||||
return {
|
||||
getDimensions: function () {
|
||||
return [max[0] - min[0], max[1] - min[1]];
|
||||
},
|
||||
getOrigin: function () {
|
||||
return min;
|
||||
},
|
||||
getDomainOffset: function () {
|
||||
return domainOffset;
|
||||
},
|
||||
getBuffers: function () {
|
||||
return buffers;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return GLPlotPreparer;
|
||||
|
||||
}
|
||||
);
|
78
platform/features/plot/src/MCTChart.js
Normal file
78
platform/features/plot/src/MCTChart.js
Normal file
@ -0,0 +1,78 @@
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining MCTChart. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
["./GLPlot"],
|
||||
function (GLPlot) {
|
||||
"use strict";
|
||||
|
||||
var TEMPLATE = "<canvas style='position: absolute; background: none; width: 100%; height: 100%;'></canvas>";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function MCTChart($interval) {
|
||||
|
||||
function linkChart(scope, element) {
|
||||
var canvas = element.find("canvas")[0],
|
||||
chart = new GLPlot(canvas);
|
||||
|
||||
function doDraw() {
|
||||
var draw = scope.draw;
|
||||
|
||||
canvas.width = canvas.offsetWidth;
|
||||
canvas.height = canvas.offsetHeight;
|
||||
chart.clear();
|
||||
|
||||
if (!draw) {
|
||||
return;
|
||||
}
|
||||
|
||||
chart.setDimensions(
|
||||
draw.dimensions || [1, 1],
|
||||
draw.origin || [0, 0]
|
||||
);
|
||||
|
||||
(draw.lines || []).forEach(function (line) {
|
||||
chart.drawLine(
|
||||
line.buffer,
|
||||
line.color,
|
||||
line.points
|
||||
);
|
||||
});
|
||||
|
||||
(draw.boxes || []).forEach(function (box) {
|
||||
chart.drawSquare(
|
||||
box.start,
|
||||
box.end,
|
||||
box.color
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function drawIfResized() {
|
||||
if (canvas.width !== canvas.offsetWidth ||
|
||||
canvas.height !== canvas.offsetHeight) {
|
||||
doDraw();
|
||||
}
|
||||
}
|
||||
|
||||
$interval(drawIfResized, 1000);
|
||||
scope.$watchCollection("draw", doDraw);
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: "E",
|
||||
template: TEMPLATE,
|
||||
link: linkChart,
|
||||
scope: { draw: "=" }
|
||||
};
|
||||
}
|
||||
|
||||
return MCTChart;
|
||||
}
|
||||
);
|
270
platform/features/plot/src/PlotController.js
Normal file
270
platform/features/plot/src/PlotController.js
Normal file
@ -0,0 +1,270 @@
|
||||
/*global define,moment,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining PlotController. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
["./GLPlotPreparer", "./PlotPalette", "../lib/moment.min.js"],
|
||||
function (GLPlotPreparer, PlotPalette) {
|
||||
"use strict";
|
||||
|
||||
var AXIS_DEFAULTS = [
|
||||
{ "name": "Time" },
|
||||
{ "name": "Value" }
|
||||
],
|
||||
DOMAIN_TICKS = 5,
|
||||
RANGE_TICKS = 7;
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function PlotController($scope) {
|
||||
var mousePosition,
|
||||
marqueeStart,
|
||||
panZoomStack = [{
|
||||
dimensions: [],
|
||||
origin: []
|
||||
}],
|
||||
domainOffset;
|
||||
|
||||
function formatDomainValue(v) {
|
||||
return moment.utc(v).format("YYYY-DDD HH:mm:ss");
|
||||
}
|
||||
|
||||
function formatRangeValue(v) {
|
||||
return v.toFixed(1);
|
||||
}
|
||||
|
||||
// Utility, for map/forEach loops. Index 0 is domain,
|
||||
// index 1 is range.
|
||||
function formatValue(v, i) {
|
||||
return (i ? formatRangeValue : formatDomainValue)(v);
|
||||
}
|
||||
|
||||
|
||||
function pixelToDomainRange(x, y, width, height, domainOffset) {
|
||||
var panZoom = panZoomStack[panZoomStack.length - 1],
|
||||
offset = [ domainOffset || 0, 0],
|
||||
origin = panZoom.origin,
|
||||
dimensions = panZoom.dimensions;
|
||||
|
||||
if (!dimensions || !origin) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [ x / width, (height - y) / height ].map(function (v, i) {
|
||||
return v * dimensions[i] + origin[i] + offset[i];
|
||||
});
|
||||
}
|
||||
|
||||
function mousePositionToDomainRange(mousePosition, domainOffset) {
|
||||
return pixelToDomainRange(
|
||||
mousePosition.x,
|
||||
mousePosition.y,
|
||||
mousePosition.width,
|
||||
mousePosition.height,
|
||||
domainOffset
|
||||
);
|
||||
}
|
||||
|
||||
function generateTicks(start, span, count, format) {
|
||||
var step = span / (count - 1),
|
||||
result = [],
|
||||
i;
|
||||
|
||||
for (i = 0; i < count; i += 1) {
|
||||
result.push({
|
||||
label: format(i * step + start)
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function updateMarqueeBox() {
|
||||
$scope.draw.boxes = marqueeStart ?
|
||||
[{
|
||||
start: mousePositionToDomainRange(marqueeStart),
|
||||
end: mousePositionToDomainRange(mousePosition),
|
||||
color: [1, 1, 1, 0.5 ]
|
||||
}] : undefined;
|
||||
}
|
||||
|
||||
function updateDrawingBounds() {
|
||||
var panZoom = panZoomStack[panZoomStack.length - 1];
|
||||
|
||||
$scope.draw.dimensions = panZoom.dimensions;
|
||||
$scope.draw.origin = panZoom.origin;
|
||||
}
|
||||
|
||||
|
||||
function plotTelemetry() {
|
||||
var telemetry, prepared, data;
|
||||
|
||||
telemetry = $scope.telemetry;
|
||||
|
||||
if (!telemetry) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = telemetry.getResponse();
|
||||
|
||||
prepared = new GLPlotPreparer(
|
||||
data,
|
||||
($scope.axes[0].active || {}).key,
|
||||
($scope.axes[1].active || {}).key
|
||||
);
|
||||
|
||||
$scope.axes[0].ticks = generateTicks(
|
||||
prepared.getOrigin()[0] + prepared.getDomainOffset(),
|
||||
prepared.getDimensions()[0],
|
||||
DOMAIN_TICKS,
|
||||
formatDomainValue
|
||||
);
|
||||
$scope.axes[1].ticks = generateTicks(
|
||||
prepared.getOrigin()[1],
|
||||
prepared.getDimensions()[1],
|
||||
RANGE_TICKS,
|
||||
formatRangeValue
|
||||
);
|
||||
|
||||
panZoomStack[0] = {
|
||||
origin: prepared.getOrigin(),
|
||||
dimensions: prepared.getDimensions()
|
||||
};
|
||||
|
||||
domainOffset = prepared.getDomainOffset();
|
||||
|
||||
$scope.draw.lines = prepared.getBuffers().map(function (buf, i) {
|
||||
return {
|
||||
buffer: buf,
|
||||
color: PlotPalette.getFloatColor(i),
|
||||
points: buf.length / 2
|
||||
};
|
||||
});
|
||||
|
||||
updateDrawingBounds();
|
||||
updateMarqueeBox();
|
||||
}
|
||||
|
||||
function setupAxes(metadatas) {
|
||||
var domainKeys = {},
|
||||
rangeKeys = {},
|
||||
domains = [],
|
||||
ranges = [];
|
||||
|
||||
function buildOptionsForMetadata(m) {
|
||||
(m.domains || []).forEach(function (domain) {
|
||||
if (!domainKeys[domain.key]) {
|
||||
domainKeys[domain.key] = true;
|
||||
domains.push(domain);
|
||||
}
|
||||
});
|
||||
(m.ranges || []).forEach(function (range) {
|
||||
if (!rangeKeys[range.key]) {
|
||||
rangeKeys[range.key] = true;
|
||||
ranges.push(range);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
(metadatas || []).
|
||||
forEach(buildOptionsForMetadata);
|
||||
|
||||
[domains, ranges].forEach(function (options, i) {
|
||||
var active = $scope.axes[i].active;
|
||||
$scope.axes[i].options = options;
|
||||
if (!active || !active.key) {
|
||||
$scope.axes[i].active =
|
||||
options[0] || AXIS_DEFAULTS[i];
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function toMousePosition($event) {
|
||||
var target = $event.target,
|
||||
bounds = target.getBoundingClientRect();
|
||||
|
||||
return {
|
||||
x: $event.clientX - bounds.left,
|
||||
y: $event.clientY - bounds.top,
|
||||
width: bounds.width,
|
||||
height: bounds.height
|
||||
};
|
||||
}
|
||||
|
||||
function marqueeZoom(start, end) {
|
||||
var a = mousePositionToDomainRange(start),
|
||||
b = 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]
|
||||
];
|
||||
|
||||
panZoomStack.push({
|
||||
origin: origin,
|
||||
dimensions: dimensions
|
||||
});
|
||||
}
|
||||
|
||||
$scope.axes = [ {}, {} ];
|
||||
$scope.$watch("telemetry.getMetadata()", setupAxes);
|
||||
$scope.$on("telemetryUpdate", plotTelemetry);
|
||||
$scope.draw = {};
|
||||
|
||||
return {
|
||||
getColor: function (index) {
|
||||
return PlotPalette.getStringColor(index);
|
||||
},
|
||||
getHoverCoordinates: function () {
|
||||
return mousePosition ?
|
||||
mousePositionToDomainRange(
|
||||
mousePosition,
|
||||
domainOffset
|
||||
).map(formatValue) : [];
|
||||
},
|
||||
hover: function ($event) {
|
||||
mousePosition = toMousePosition($event);
|
||||
if (marqueeStart) {
|
||||
updateMarqueeBox();
|
||||
}
|
||||
},
|
||||
startMarquee: function ($event) {
|
||||
mousePosition = marqueeStart = toMousePosition($event);
|
||||
updateMarqueeBox();
|
||||
},
|
||||
endMarquee: function ($event) {
|
||||
mousePosition = toMousePosition($event);
|
||||
if (marqueeStart) {
|
||||
marqueeZoom(marqueeStart, mousePosition);
|
||||
marqueeStart = undefined;
|
||||
updateMarqueeBox();
|
||||
updateDrawingBounds();
|
||||
}
|
||||
},
|
||||
isZoomed: function () {
|
||||
return panZoomStack.length > 1;
|
||||
},
|
||||
stepBackPanZoom: function () {
|
||||
if (panZoomStack.length > 1) {
|
||||
panZoomStack.pop();
|
||||
updateDrawingBounds();
|
||||
}
|
||||
},
|
||||
unzoom: function () {
|
||||
panZoomStack = [panZoomStack[0]];
|
||||
updateDrawingBounds();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
return PlotController;
|
||||
}
|
||||
);
|
65
platform/features/plot/src/PlotPalette.js
Normal file
65
platform/features/plot/src/PlotPalette.js
Normal file
@ -0,0 +1,65 @@
|
||||
/*global define*/
|
||||
|
||||
/**
|
||||
* Plot palette. Defines colors for various plot lines.
|
||||
*/
|
||||
define(
|
||||
function () {
|
||||
'use strict';
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
return {
|
||||
getIntegerColor: function (i) {
|
||||
return integerPalette[Math.floor(i) % integerPalette.length];
|
||||
},
|
||||
getFloatColor: function (i) {
|
||||
return floatPalette[Math.floor(i) % floatPalette.length];
|
||||
},
|
||||
getStringColor: function (i) {
|
||||
return stringPalette[Math.floor(i) % stringPalette.length];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user