mirror of
https://github.com/nasa/openmct.git
synced 2025-01-19 19:27:07 +00:00
[Plot] Add JSDoc
Add inline comments to Plot scripts. WTD-533.
This commit is contained in:
parent
870172ec6f
commit
622f1f8be7
@ -1,4 +1,4 @@
|
||||
/*global define,Promise,Float32Array*/
|
||||
/*global define,Float32Array*/
|
||||
|
||||
/**
|
||||
* Module defining GLPlot. Created by vwoeltje on 11/12/14.
|
||||
@ -8,6 +8,7 @@ define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
// WebGL shader sources (for drawing plain colors)
|
||||
var FRAGMENT_SHADER = [
|
||||
"precision mediump float;",
|
||||
"uniform vec4 uColor;",
|
||||
@ -24,6 +25,13 @@ define(
|
||||
"}"
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Create a new chart which uses WebGL for rendering.
|
||||
*
|
||||
* @constructor
|
||||
* @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") || canvas.getContext("experimental-webgl"),
|
||||
vertexShader,
|
||||
@ -35,6 +43,7 @@ define(
|
||||
uOrigin,
|
||||
buffer;
|
||||
|
||||
// Ensure a context was actually available before proceeding
|
||||
if (!gl) {
|
||||
throw new Error("WebGL unavailable.");
|
||||
}
|
||||
@ -47,25 +56,33 @@ define(
|
||||
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");
|
||||
gl.enableVertexAttribArray(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
|
||||
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);
|
||||
|
||||
// Utility function to handle drawing of a buffer;
|
||||
// drawType will determine whether this is a box, line, etc.
|
||||
function doDraw(drawType, buf, color, points) {
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, buf, gl.DYNAMIC_DRAW);
|
||||
@ -75,23 +92,51 @@ define(
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Clear the chart.
|
||||
*/
|
||||
clear: function () {
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT);
|
||||
},
|
||||
/**
|
||||
* Set the logical boundaries of the chart.
|
||||
* @param {number[]} dimensions the horizontal and
|
||||
* vertical dimensions of the chart
|
||||
* @param {number[]} origin the horizontal/vertical
|
||||
* origin of the chart
|
||||
*/
|
||||
setDimensions: function (dimensions, origin) {
|
||||
gl.uniform2fv(uDimensions, dimensions);
|
||||
gl.uniform2fv(uOrigin, origin);
|
||||
},
|
||||
/**
|
||||
* Draw the supplied buffer as a line strip (a sequence
|
||||
* of line segments), in the chosen color.
|
||||
* @param {Float32Array} buf the line strip to draw,
|
||||
* in alternating x/y positions
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the line, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
* @param {number} points the number of points to draw
|
||||
*/
|
||||
drawLine: function (buf, color, points) {
|
||||
doDraw(gl.LINE_STRIP, buf, color, points);
|
||||
},
|
||||
/**
|
||||
* Draw a rectangle extending from one corner to another,
|
||||
* in the chosen color.
|
||||
* @param {number[]} min the first corner of the rectangle
|
||||
* @param {number[]} max the opposite corner
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the rectangle, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
*/
|
||||
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 GLChart;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*global define,Promise*/
|
||||
/*global define*/
|
||||
|
||||
/**
|
||||
* Module defining MCTChart. Created by vwoeltje on 11/12/14.
|
||||
@ -11,6 +11,34 @@ define(
|
||||
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
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
@ -30,20 +58,29 @@ define(
|
||||
return;
|
||||
}
|
||||
|
||||
// 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,
|
||||
@ -52,6 +89,7 @@ define(
|
||||
);
|
||||
});
|
||||
|
||||
// Draw boxes (e.g. marquee zoom rect)
|
||||
(draw.boxes || []).forEach(function (box) {
|
||||
chart.drawSquare(
|
||||
box.start,
|
||||
@ -62,6 +100,9 @@ define(
|
||||
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@ -69,14 +110,25 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
// Check for resize, on a timer
|
||||
$interval(drawIfResized, 1000);
|
||||
|
||||
// Watch "draw" for external changes to the set of
|
||||
// things to be drawn.
|
||||
scope.$watchCollection("draw", doDraw);
|
||||
}
|
||||
|
||||
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: "=" }
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*global define,moment,Promise*/
|
||||
/*global define*/
|
||||
|
||||
/**
|
||||
* Module defining PlotController. Created by vwoeltje on 11/12/14.
|
||||
@ -32,6 +32,13 @@ define(
|
||||
RANGE_TICKS = 7;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
@ -50,6 +57,8 @@ define(
|
||||
formatter.formatDomainValue)(v);
|
||||
}
|
||||
|
||||
// Converts from pixel coordinates to domain-range,
|
||||
// to interpret mouse gestures.
|
||||
function mousePositionToDomainRange(mousePosition) {
|
||||
return new PlotPosition(
|
||||
mousePosition.x,
|
||||
@ -60,11 +69,36 @@ define(
|
||||
).getPosition();
|
||||
}
|
||||
|
||||
// Utility function to get the mouse position (in x,y
|
||||
// pixel coordinates in the canvas area) from a mouse
|
||||
// event object.
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
// 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.)
|
||||
function toDisplayable(position) {
|
||||
return [ position[0] - domainOffset, position[1] ];
|
||||
}
|
||||
|
||||
// Update the drawable marquee area to reflect current
|
||||
// mouse position (or don't show it at all, if no marquee
|
||||
// zoom is in progress)
|
||||
function updateMarqueeBox() {
|
||||
// Express this as a box in the draw object, which
|
||||
// is passed to an mct-chart in the template for rendering.
|
||||
$scope.draw.boxes = marqueeStart ?
|
||||
[{
|
||||
start: toDisplayable(mousePositionToDomainRange(marqueeStart)),
|
||||
@ -73,9 +107,12 @@ define(
|
||||
}] : undefined;
|
||||
}
|
||||
|
||||
// Update the bounds (origin and dimensions) of the drawing area.
|
||||
function updateDrawingBounds() {
|
||||
var panZoom = panZoomStack.getPanZoom();
|
||||
|
||||
// Communicate pan-zoom state from stack to the draw object
|
||||
// which is passed to mct-chart in the template.
|
||||
$scope.draw.dimensions = panZoom.dimensions;
|
||||
$scope.draw.origin = [
|
||||
panZoom.origin[0] - domainOffset,
|
||||
@ -83,6 +120,7 @@ define(
|
||||
];
|
||||
}
|
||||
|
||||
// Update tick marks in scope.
|
||||
function updateTicks() {
|
||||
var tickGenerator = new PlotTickGenerator(panZoomStack, formatter);
|
||||
|
||||
@ -92,6 +130,8 @@ define(
|
||||
tickGenerator.generateRangeTicks(RANGE_TICKS);
|
||||
}
|
||||
|
||||
// Populate the scope with axis information (specifically, options
|
||||
// available for each axis.)
|
||||
function setupAxes(metadatas) {
|
||||
$scope.axes = [
|
||||
new PlotAxis("domain", metadatas, AXIS_DEFAULTS[0]),
|
||||
@ -99,34 +139,48 @@ define(
|
||||
];
|
||||
}
|
||||
|
||||
// Respond to newly-available telemetry data; update the
|
||||
// drawing area accordingly.
|
||||
function plotTelemetry() {
|
||||
var prepared, data, telemetry;
|
||||
var prepared, datas, telemetry;
|
||||
|
||||
// Get a reference to the TelemetryController
|
||||
telemetry = $scope.telemetry;
|
||||
|
||||
// Nothing to plot without TelemetryController
|
||||
if (!telemetry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure axes have been initialized (we will want to
|
||||
// get the active axis below)
|
||||
if (!$scope.axes) {
|
||||
setupAxes(telemetry.getMetadata());
|
||||
}
|
||||
|
||||
data = telemetry.getResponse();
|
||||
// Get data sets
|
||||
datas = telemetry.getResponse();
|
||||
|
||||
// Prepare data sets for rendering
|
||||
prepared = new PlotPreparer(
|
||||
data,
|
||||
datas,
|
||||
($scope.axes[0].active || {}).key,
|
||||
($scope.axes[1].active || {}).key
|
||||
);
|
||||
|
||||
// Fit to the boundaries of the data, but don't
|
||||
// override any user-initiated pan-zoom changes.
|
||||
panZoomStack.setBasePanZoom(
|
||||
prepared.getOrigin(),
|
||||
prepared.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.
|
||||
domainOffset = prepared.getDomainOffset();
|
||||
|
||||
// Draw the buffers. Select color by index.
|
||||
$scope.draw.lines = prepared.getBuffers().map(function (buf, i) {
|
||||
return {
|
||||
buffer: buf,
|
||||
@ -140,19 +194,11 @@ define(
|
||||
updateTicks();
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
// 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 = mousePositionToDomainRange(start),
|
||||
b = mousePositionToDomainRange(end),
|
||||
origin = [
|
||||
@ -164,7 +210,10 @@ define(
|
||||
Math.max(a[1], b[1]) - origin[1]
|
||||
];
|
||||
|
||||
// Push the new state onto the pan-zoom stack
|
||||
panZoomStack.pushPanZoom(origin, dimensions);
|
||||
|
||||
// Make sure tick marks reflect new bounds
|
||||
updateTicks();
|
||||
}
|
||||
|
||||
@ -173,25 +222,49 @@ define(
|
||||
$scope.draw = {};
|
||||
|
||||
return {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
getColor: function (index) {
|
||||
return PlotPalette.getStringColor(index);
|
||||
},
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
getHoverCoordinates: function () {
|
||||
return mousePosition ?
|
||||
mousePositionToDomainRange(
|
||||
mousePosition
|
||||
).map(formatValue) : [];
|
||||
},
|
||||
/**
|
||||
* Handle mouse movement over the chart area.
|
||||
* @param $event the mouse event
|
||||
*/
|
||||
hover: function ($event) {
|
||||
mousePosition = toMousePosition($event);
|
||||
if (marqueeStart) {
|
||||
updateMarqueeBox();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Initiate a marquee zoom action.
|
||||
* @param $event the mouse event
|
||||
*/
|
||||
startMarquee: function ($event) {
|
||||
mousePosition = marqueeStart = toMousePosition($event);
|
||||
updateMarqueeBox();
|
||||
},
|
||||
/**
|
||||
* Complete a marquee zoom action.
|
||||
* @param $event the mouse event
|
||||
*/
|
||||
endMarquee: function ($event) {
|
||||
mousePosition = toMousePosition($event);
|
||||
if (marqueeStart) {
|
||||
@ -201,13 +274,26 @@ define(
|
||||
updateDrawingBounds();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
isZoomed: function () {
|
||||
return panZoomStack.getDepth() > 1;
|
||||
},
|
||||
/**
|
||||
* Undo the most recent pan/zoom change and restore
|
||||
* the prior state.
|
||||
*/
|
||||
stepBackPanZoom: function () {
|
||||
panZoomStack.popPanZoom();
|
||||
updateDrawingBounds();
|
||||
},
|
||||
/**
|
||||
* Undo all pan/zoom changes and restore the initial state.
|
||||
*/
|
||||
unzoom: function () {
|
||||
panZoomStack.clearPanZoom();
|
||||
updateDrawingBounds();
|
||||
|
@ -5,10 +5,30 @@ define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A PlotAxis provides a template-ready set of options
|
||||
* for the domain or range axis, sufficient to populate
|
||||
* selectors.
|
||||
*
|
||||
* @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) {
|
||||
var keys = {},
|
||||
options = [];
|
||||
|
||||
// Look through all metadata objects and assemble a list
|
||||
// of all possible domain or range options
|
||||
function buildOptionsForMetadata(m) {
|
||||
(m[axisType] || []).forEach(function (option) {
|
||||
if (!keys[option.key]) {
|
||||
@ -24,7 +44,18 @@ define(
|
||||
// template, so expose properties directly to facilitate
|
||||
// two-way data binding (for drop-down menus)
|
||||
return {
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
options: options,
|
||||
/**
|
||||
* The currently chosen option for this axis. An
|
||||
* initial value is provided; this will be updated
|
||||
* directly form the plot template.
|
||||
*/
|
||||
active: options[0] || defaultValue
|
||||
};
|
||||
}
|
||||
|
@ -5,8 +5,15 @@ define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
// Date format to use for domain values; in particular,
|
||||
// use day-of-year instead of month/day
|
||||
var DATE_FORMAT = "YYYY-DDD HH:mm:ss";
|
||||
|
||||
/**
|
||||
* The PlotFormatter is responsible for formatting (as text
|
||||
* for display) values along either the domain or range of a
|
||||
* plot.
|
||||
*/
|
||||
function PlotFormatter() {
|
||||
function formatDomainValue(v) {
|
||||
return moment.utc(v).format(DATE_FORMAT);
|
||||
@ -17,7 +24,20 @@ define(
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Format a domain value.
|
||||
* @param {number} v the domain value; a timestamp
|
||||
* in milliseconds since start of 1970
|
||||
* @returns {string} a textual representation of the
|
||||
* data and time, suitable for display.
|
||||
*/
|
||||
formatDomainValue: formatDomainValue,
|
||||
/**
|
||||
* Format a range value.
|
||||
* @param {number} v the range value; a numeric value
|
||||
* @returns {string} a textual representation of the
|
||||
* value, suitable for display.
|
||||
*/
|
||||
formatRangeValue: formatRangeValue
|
||||
};
|
||||
}
|
||||
|
@ -52,18 +52,57 @@ define(
|
||||
}).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.
|
||||
* @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];
|
||||
};
|
||||
|
@ -5,9 +5,30 @@ define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
var 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.
|
||||
function getDepth() {
|
||||
return stack.length;
|
||||
}
|
||||
@ -43,13 +64,70 @@ define(
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
getDepth: getDepth,
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
pushPanZoom: pushPanZoom,
|
||||
|
||||
/**
|
||||
* 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.)
|
||||
*/
|
||||
popPanZoom: popPanZoom,
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
setBasePanZoom: setBasePanZoom,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
clearPanZoom: clearPanZoom,
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
getPanZoom: getPanZoom,
|
||||
|
||||
/**
|
||||
* Get the current origin, as represented on the top of the
|
||||
* stack.
|
||||
* @returns {number[]} the current plot origin
|
||||
*/
|
||||
getOrigin: getOrigin,
|
||||
|
||||
/**
|
||||
* Get the current dimensions, as represented on the top of
|
||||
* the stack.
|
||||
* @returns {number[]} the current plot dimensions
|
||||
*/
|
||||
getDimensions: getDimensions
|
||||
};
|
||||
}
|
||||
|
@ -5,27 +5,65 @@ define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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,
|
||||
position;
|
||||
|
||||
function convert(v, i) {
|
||||
return v * dimensions[i] + origin[i];
|
||||
}
|
||||
|
||||
if (!dimensions || !origin) {
|
||||
// We need both dimensions and origin to compute a position
|
||||
position = [];
|
||||
} else {
|
||||
position = [ x / width, (height - y) / height ].map(function (v, i) {
|
||||
return v * dimensions[i] + origin[i];
|
||||
});
|
||||
// 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)
|
||||
position = [ x / width, (height - y) / height ].map(convert);
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Get the domain value corresponding to this pixel position.
|
||||
* @returns {number} the domain value
|
||||
*/
|
||||
getDomain: function () {
|
||||
return position[0];
|
||||
},
|
||||
/**
|
||||
* Get the range value corresponding to this pixel position.
|
||||
* @returns {number} the range value
|
||||
*/
|
||||
getRange: function () {
|
||||
return 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
|
||||
*/
|
||||
getPosition: function () {
|
||||
return position;
|
||||
}
|
||||
|
@ -10,6 +10,16 @@ define(
|
||||
|
||||
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).
|
||||
* @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 = [],
|
||||
@ -23,14 +33,18 @@ define(
|
||||
// Remove any undefined data sets
|
||||
datas = (datas || []).filter(identity);
|
||||
|
||||
// Filter out un
|
||||
// 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 = index + 1) {
|
||||
for (index = 0; index < data.getPointCount(); index += 1) {
|
||||
x = data.getDomainValue(index, domain);
|
||||
y = data.getRangeValue(index, range);
|
||||
vertices[i].push(x - domainOffset);
|
||||
@ -42,23 +56,62 @@ define(
|
||||
}
|
||||
});
|
||||
|
||||
// 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
|
||||
buffers = vertices.map(function (v) { return new Float32Array(v); });
|
||||
|
||||
return {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
getDimensions: function () {
|
||||
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
|
||||
*/
|
||||
getOrigin: function () {
|
||||
return 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
|
||||
*/
|
||||
getDomainOffset: function () {
|
||||
return 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
|
||||
*/
|
||||
getBuffers: function () {
|
||||
return buffers;
|
||||
}
|
||||
|
@ -5,8 +5,22 @@ define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* The PlotTickGenerator provides labels for ticks along the
|
||||
* domain and range axes of the plot, to support the plot
|
||||
* template.
|
||||
*
|
||||
* @constructor
|
||||
* @param {PlotPanZoomStack} panZoomStack the pan-zoom stack for
|
||||
* this plot, used to determine plot boundaries
|
||||
* @param {PlotFormatter} formatter used to format (for display)
|
||||
* domain and range values.
|
||||
*/
|
||||
function PlotTickGenerator(panZoomStack, formatter) {
|
||||
|
||||
// Generate ticks; interpolate from start up to
|
||||
// start + span in count steps, using the provided
|
||||
// formatter to represent each value.
|
||||
function generateTicks(start, span, count, format) {
|
||||
var step = span / (count - 1),
|
||||
result = [],
|
||||
@ -23,6 +37,11 @@ define(
|
||||
|
||||
|
||||
return {
|
||||
/**
|
||||
* Generate tick marks for the domain axis.
|
||||
* @param {number} count the number of ticks
|
||||
* @returns {string[]} labels for those ticks
|
||||
*/
|
||||
generateDomainTicks: function (count) {
|
||||
var panZoom = panZoomStack.getPanZoom();
|
||||
return generateTicks(
|
||||
@ -32,6 +51,12 @@ define(
|
||||
formatter.formatDomainValue
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate tick marks for the range axis.
|
||||
* @param {number} count the number of ticks
|
||||
* @returns {string[]} labels for those ticks
|
||||
*/
|
||||
generateRangeTicks: function (count) {
|
||||
var panZoom = panZoomStack.getPanZoom();
|
||||
return generateTicks(
|
||||
|
Loading…
Reference in New Issue
Block a user