mirror of
https://github.com/nasa/openmct.git
synced 2025-06-11 03:41:37 +00:00
[Code Style] Use prototypes in Plot bundle
WTD-1482.
This commit is contained in:
parent
aefad6fdd3
commit
820c15d74c
@ -31,116 +31,89 @@ define(
|
|||||||
*
|
*
|
||||||
* @memberof platform/features/plot
|
* @memberof platform/features/plot
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {platform/features/plot.Chart}
|
||||||
* @param {CanvasElement} canvas the canvas object to render upon
|
* @param {CanvasElement} canvas the canvas object to render upon
|
||||||
* @throws {Error} an error is thrown if Canvas's 2D API is unavailable.
|
* @throws {Error} an error is thrown if Canvas's 2D API is unavailable.
|
||||||
*/
|
*/
|
||||||
function Canvas2DChart(canvas) {
|
function Canvas2DChart(canvas) {
|
||||||
var c2d = canvas.getContext('2d'),
|
this.canvas = canvas;
|
||||||
width = canvas.width,
|
this.c2d = canvas.getContext('2d');
|
||||||
height = canvas.height,
|
this.width = canvas.width;
|
||||||
dimensions = [ width, height ],
|
this.height = canvas.height;
|
||||||
origin = [ 0, 0 ];
|
this.dimensions = [ this.width, this.height ];
|
||||||
|
this.origin = [ 0, 0 ];
|
||||||
|
|
||||||
// Convert from logical to physical x coordinates
|
if (!this.c2d) {
|
||||||
function x(v) {
|
|
||||||
return ((v - origin[0]) / dimensions[0]) * width;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert from logical to physical y coordinates
|
|
||||||
function y(v) {
|
|
||||||
return height - ((v - origin[1]) / dimensions[1]) * height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the color to be used for drawing operations
|
|
||||||
function setColor(color) {
|
|
||||||
var mappedColor = color.map(function (c, i) {
|
|
||||||
return i < 3 ? Math.floor(c * 255) : (c);
|
|
||||||
}).join(',');
|
|
||||||
c2d.strokeStyle = "rgba(" + mappedColor + ")";
|
|
||||||
c2d.fillStyle = "rgba(" + mappedColor + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!c2d) {
|
|
||||||
throw new Error("Canvas 2d API unavailable.");
|
throw new Error("Canvas 2d API unavailable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Clear the chart.
|
|
||||||
* @memberof platform/features/plot.Canvas2DChart#
|
|
||||||
*/
|
|
||||||
clear: function () {
|
|
||||||
width = canvas.width;
|
|
||||||
height = canvas.height;
|
|
||||||
c2d.clearRect(0, 0, width, height);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 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.Canvas2DChart#
|
|
||||||
*/
|
|
||||||
setDimensions: function (newDimensions, newOrigin) {
|
|
||||||
dimensions = newDimensions;
|
|
||||||
origin = newOrigin;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 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.Canvas2DChart#
|
|
||||||
*/
|
|
||||||
drawLine: function (buf, color, points) {
|
|
||||||
var i;
|
|
||||||
|
|
||||||
setColor(color);
|
|
||||||
|
|
||||||
// Configure context to draw two-pixel-thick lines
|
|
||||||
c2d.lineWidth = 2;
|
|
||||||
|
|
||||||
// Start a new path...
|
|
||||||
if (buf.length > 1) {
|
|
||||||
c2d.beginPath();
|
|
||||||
c2d.moveTo(x(buf[0]), y(buf[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...and add points to it...
|
|
||||||
for (i = 2; i < points * 2; i = i + 2) {
|
|
||||||
c2d.lineTo(x(buf[i]), y(buf[i + 1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...before finally drawing it.
|
|
||||||
c2d.stroke();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 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.Canvas2DChart#
|
|
||||||
*/
|
|
||||||
drawSquare: function (min, max, color) {
|
|
||||||
var x1 = x(min[0]),
|
|
||||||
y1 = y(min[1]),
|
|
||||||
w = x(max[0]) - x1,
|
|
||||||
h = y(max[1]) - y1;
|
|
||||||
|
|
||||||
setColor(color);
|
|
||||||
c2d.fillRect(x1, y1, w, h);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
return Canvas2DChart;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -51,6 +51,7 @@ define(
|
|||||||
*
|
*
|
||||||
* @memberof platform/features/plot
|
* @memberof platform/features/plot
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {platform/features/plot.Chart}
|
||||||
* @param {CanvasElement} canvas the canvas object to render upon
|
* @param {CanvasElement} canvas the canvas object to render upon
|
||||||
* @throws {Error} an error is thrown if WebGL is unavailable.
|
* @throws {Error} an error is thrown if WebGL is unavailable.
|
||||||
*/
|
*/
|
||||||
@ -62,8 +63,7 @@ define(
|
|||||||
aVertexPosition,
|
aVertexPosition,
|
||||||
uColor,
|
uColor,
|
||||||
uDimensions,
|
uDimensions,
|
||||||
uOrigin,
|
uOrigin;
|
||||||
buffer;
|
|
||||||
|
|
||||||
// Ensure a context was actually available before proceeding
|
// Ensure a context was actually available before proceeding
|
||||||
if (!gl) {
|
if (!gl) {
|
||||||
@ -94,7 +94,7 @@ define(
|
|||||||
gl.enableVertexAttribArray(aVertexPosition);
|
gl.enableVertexAttribArray(aVertexPosition);
|
||||||
|
|
||||||
// Create a buffer to holds points which will be drawn
|
// Create a buffer to holds points which will be drawn
|
||||||
buffer = gl.createBuffer();
|
this.buffer = gl.createBuffer();
|
||||||
|
|
||||||
// Use a line width of 2.0 for legibility
|
// Use a line width of 2.0 for legibility
|
||||||
gl.lineWidth(2.0);
|
gl.lineWidth(2.0);
|
||||||
@ -103,79 +103,59 @@ define(
|
|||||||
gl.enable(gl.BLEND);
|
gl.enable(gl.BLEND);
|
||||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
// Utility function to handle drawing of a buffer;
|
this.gl = gl;
|
||||||
// drawType will determine whether this is a box, line, etc.
|
this.aVertexPosition = aVertexPosition;
|
||||||
function doDraw(drawType, buf, color, points) {
|
this.uColor = uColor;
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
this.uDimensions = uDimensions;
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, buf, gl.DYNAMIC_DRAW);
|
this.uOrigin = uOrigin;
|
||||||
gl.vertexAttribPointer(aVertexPosition, 2, gl.FLOAT, false, 0, 0);
|
|
||||||
gl.uniform4fv(uColor, color);
|
|
||||||
gl.drawArrays(drawType, 0, points);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Clear the chart.
|
|
||||||
* @memberof platform/features/plot.GLChart#
|
|
||||||
*/
|
|
||||||
clear: function () {
|
|
||||||
// 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);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 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.GLChart#
|
|
||||||
*/
|
|
||||||
setDimensions: function (dimensions, origin) {
|
|
||||||
if (dimensions && dimensions.length > 0 &&
|
|
||||||
origin && origin.length > 0) {
|
|
||||||
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
|
|
||||||
* @memberof platform/features/plot.GLChart#
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
* @memberof platform/features/plot.GLChart#
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
return GLChart;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -206,6 +206,45 @@ define(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
return MCTChart;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -61,15 +61,11 @@ define(
|
|||||||
throttle,
|
throttle,
|
||||||
PLOT_FIXED_DURATION
|
PLOT_FIXED_DURATION
|
||||||
) {
|
) {
|
||||||
var subPlotFactory = new SubPlotFactory(telemetryFormatter),
|
var self = this,
|
||||||
modeOptions = new PlotModeOptions([], subPlotFactory),
|
subPlotFactory = new SubPlotFactory(telemetryFormatter),
|
||||||
subplots = [],
|
|
||||||
cachedObjects = [],
|
cachedObjects = [],
|
||||||
limitTracker,
|
|
||||||
updater,
|
updater,
|
||||||
handle,
|
handle;
|
||||||
scheduleUpdate,
|
|
||||||
domainOffset;
|
|
||||||
|
|
||||||
// Populate the scope with axis information (specifically, options
|
// Populate the scope with axis information (specifically, options
|
||||||
// available for each axis.)
|
// available for each axis.)
|
||||||
@ -91,18 +87,13 @@ define(
|
|||||||
function setupModes(telemetryObjects) {
|
function setupModes(telemetryObjects) {
|
||||||
if (cachedObjects !== telemetryObjects) {
|
if (cachedObjects !== telemetryObjects) {
|
||||||
cachedObjects = telemetryObjects;
|
cachedObjects = telemetryObjects;
|
||||||
modeOptions = new PlotModeOptions(
|
self.modeOptions = new PlotModeOptions(
|
||||||
telemetryObjects || [],
|
telemetryObjects || [],
|
||||||
subPlotFactory
|
subPlotFactory
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update all sub-plots
|
|
||||||
function update() {
|
|
||||||
scheduleUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reinstantiate the plot updater (e.g. because we have a
|
// Reinstantiate the plot updater (e.g. because we have a
|
||||||
// new subscription.) This will clear the plot.
|
// new subscription.) This will clear the plot.
|
||||||
function recreateUpdater() {
|
function recreateUpdater() {
|
||||||
@ -112,7 +103,7 @@ define(
|
|||||||
($scope.axes[1].active || {}).key,
|
($scope.axes[1].active || {}).key,
|
||||||
PLOT_FIXED_DURATION
|
PLOT_FIXED_DURATION
|
||||||
);
|
);
|
||||||
limitTracker = new PlotLimitTracker(
|
self.limitTracker = new PlotLimitTracker(
|
||||||
handle,
|
handle,
|
||||||
($scope.axes[1].active || {}).key
|
($scope.axes[1].active || {}).key
|
||||||
);
|
);
|
||||||
@ -125,19 +116,19 @@ define(
|
|||||||
}
|
}
|
||||||
if (updater) {
|
if (updater) {
|
||||||
updater.update();
|
updater.update();
|
||||||
modeOptions.getModeHandler().plotTelemetry(updater);
|
self.modeOptions.getModeHandler().plotTelemetry(updater);
|
||||||
}
|
}
|
||||||
if (limitTracker) {
|
if (self.limitTracker) {
|
||||||
limitTracker.update();
|
self.limitTracker.update();
|
||||||
}
|
}
|
||||||
update();
|
self.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display new historical data as it becomes available
|
// Display new historical data as it becomes available
|
||||||
function addHistoricalData(domainObject, series) {
|
function addHistoricalData(domainObject, series) {
|
||||||
updater.addHistorical(domainObject, series);
|
updater.addHistorical(domainObject, series);
|
||||||
modeOptions.getModeHandler().plotTelemetry(updater);
|
self.modeOptions.getModeHandler().plotTelemetry(updater);
|
||||||
update();
|
self.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue a new request for historical telemetry
|
// Issue a new request for historical telemetry
|
||||||
@ -174,116 +165,119 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.modeOptions = new PlotModeOptions([], subPlotFactory);
|
||||||
|
this.updateValues = updateValues;
|
||||||
|
|
||||||
|
// Create a throttled update function
|
||||||
|
this.scheduleUpdate = throttle(function () {
|
||||||
|
self.modeOptions.getModeHandler().getSubPlots()
|
||||||
|
.forEach(updateSubplot);
|
||||||
|
});
|
||||||
|
|
||||||
// Subscribe to telemetry when a domain object becomes available
|
// Subscribe to telemetry when a domain object becomes available
|
||||||
$scope.$watch('domainObject', subscribe);
|
$scope.$watch('domainObject', subscribe);
|
||||||
|
|
||||||
// Unsubscribe when the plot is destroyed
|
// Unsubscribe when the plot is destroyed
|
||||||
$scope.$on("$destroy", releaseSubscription);
|
$scope.$on("$destroy", releaseSubscription);
|
||||||
|
|
||||||
// Create a throttled update function
|
|
||||||
scheduleUpdate = throttle(function () {
|
|
||||||
modeOptions.getModeHandler().getSubPlots()
|
|
||||||
.forEach(updateSubplot);
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
isZoomed: function () {
|
|
||||||
return modeOptions.getModeHandler().isZoomed();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Undo the most recent pan/zoom change and restore
|
|
||||||
* the prior state.
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
stepBackPanZoom: function () {
|
|
||||||
return modeOptions.getModeHandler().stepBackPanZoom();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Undo all pan/zoom changes and restore the initial state.
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
unzoom: function () {
|
|
||||||
return modeOptions.getModeHandler().unzoom();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Get the mode options (Stacked/Overlaid) that are applicable
|
|
||||||
* for this plot.
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
getModeOptions: function () {
|
|
||||||
return modeOptions.getModeOptions();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Get the current mode that is applicable to this plot. This
|
|
||||||
* will include key, name, and glyph fields.
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
getMode: function () {
|
|
||||||
return modeOptions.getMode();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Set the mode which should be active in this plot.
|
|
||||||
* @param mode one of the mode options returned from
|
|
||||||
* getModeOptions()
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
setMode: function (mode) {
|
|
||||||
modeOptions.setMode(mode);
|
|
||||||
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
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
getSubPlots: function () {
|
|
||||||
return 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
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
getLegendClass: function (telemetryObject) {
|
|
||||||
return limitTracker &&
|
|
||||||
limitTracker.getLegendClass(telemetryObject);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Explicitly update all plots.
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
update: update,
|
|
||||||
/**
|
|
||||||
* Check if a request is pending (to show the wait spinner)
|
|
||||||
* @memberof platform/features/plot.PlotController#
|
|
||||||
*/
|
|
||||||
isRequestPending: function () {
|
|
||||||
// Placeholder; this should reflect request state
|
|
||||||
// when requesting historical telemetry
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 glyph 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 false;
|
||||||
|
};
|
||||||
|
|
||||||
return PlotController;
|
return PlotController;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -50,141 +50,278 @@ define(
|
|||||||
// We are used from a template often, so maintain
|
// We are used from a template often, so maintain
|
||||||
// state in local variables to allow for fast look-up,
|
// state in local variables to allow for fast look-up,
|
||||||
// as is normal for controllers.
|
// as is normal for controllers.
|
||||||
var draw = {},
|
this.telemetryObjects = telemetryObjects;
|
||||||
rangeTicks = [],
|
this.domainTicks = [];
|
||||||
domainTicks = [],
|
this.rangeTicks = [];
|
||||||
formatter = telemetryFormatter,
|
this.formatter = telemetryFormatter;
|
||||||
domainOffset,
|
this.draw = {};
|
||||||
mousePosition,
|
this.hovering = false;
|
||||||
marqueeStart,
|
this.panZoomStack = panZoomStack;
|
||||||
panStart,
|
|
||||||
panStartBounds,
|
// Start with the right initial drawing bounds,
|
||||||
subPlotBounds,
|
// tick marks
|
||||||
hoverCoordinates,
|
this.updateDrawingBounds();
|
||||||
isHovering = false;
|
this.updateTicks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
// Utility, for map/forEach loops. Index 0 is domain,
|
||||||
// index 1 is range.
|
// index 1 is range.
|
||||||
function formatValue(v, i) {
|
function formatValue(v, i) {
|
||||||
return (i ?
|
return (i ?
|
||||||
formatter.formatRangeValue :
|
formatter.formatRangeValue :
|
||||||
formatter.formatDomainValue)(v);
|
formatter.formatDomainValue)(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function for filtering out empty strings.
|
this.hoverCoordinates = this.mousePosition &&
|
||||||
function isNonEmpty(v) {
|
this.mousePositionToDomainRange(this.mousePosition)
|
||||||
return typeof v === 'string' && v !== "";
|
.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);
|
||||||
|
this.updateHoverCoordinates();
|
||||||
|
if (this.marqueeStart) {
|
||||||
|
this.updateMarqueeBox();
|
||||||
}
|
}
|
||||||
|
if (this.panStart) {
|
||||||
// Converts from pixel coordinates to domain-range,
|
this.updatePan();
|
||||||
// to interpret mouse gestures.
|
this.updateDrawingBounds();
|
||||||
function mousePositionToDomainRange(mousePosition) {
|
this.updateTicks();
|
||||||
return new PlotPosition(
|
|
||||||
mousePosition.x,
|
|
||||||
mousePosition.y,
|
|
||||||
mousePosition.width,
|
|
||||||
mousePosition.height,
|
|
||||||
panZoomStack
|
|
||||||
).getPosition();
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Utility function to get the mouse position (in x,y
|
/**
|
||||||
// pixel coordinates in the canvas area) from a mouse
|
* Continue a previously-start pan or zoom gesture.
|
||||||
// event object.
|
* @param $event the mouse event
|
||||||
function toMousePosition($event) {
|
* @memberof platform/features/plot.SubPlot#
|
||||||
var bounds = subPlotBounds;
|
*/
|
||||||
|
SubPlot.prototype.continueDrag = function ($event) {
|
||||||
return {
|
this.mousePosition = this.toMousePosition($event);
|
||||||
x: $event.clientX - bounds.left,
|
if (this.marqueeStart) {
|
||||||
y: $event.clientY - bounds.top,
|
this.updateMarqueeBox();
|
||||||
width: bounds.width,
|
|
||||||
height: bounds.height
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
if (this.panStart) {
|
||||||
// Convert a domain-range position to a displayable
|
this.updatePan();
|
||||||
// position. This will subtract the domain offset, which
|
this.updateDrawingBounds();
|
||||||
// is used to bias domain values to minimize loss-of-precision
|
this.updateTicks();
|
||||||
// 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 currnet hover coordinates
|
* Initiate a marquee zoom action.
|
||||||
function updateHoverCoordinates() {
|
* @param $event the mouse event
|
||||||
hoverCoordinates = mousePosition &&
|
*/
|
||||||
mousePositionToDomainRange(mousePosition)
|
SubPlot.prototype.startDrag = function ($event) {
|
||||||
.map(formatValue)
|
this.subPlotBounds = $event.target.getBoundingClientRect();
|
||||||
.filter(isNonEmpty)
|
this.mousePosition = this.toMousePosition($event);
|
||||||
.join(", ");
|
// Treat any modifier key as a pan
|
||||||
}
|
if ($event.altKey || $event.shiftKey || $event.ctrlKey) {
|
||||||
|
// Start panning
|
||||||
// Update the drawable marquee area to reflect current
|
this.panStart = this.mousePosition;
|
||||||
// mouse position (or don't show it at all, if no marquee
|
this.panStartBounds = this.panZoomStack.getPanZoom();
|
||||||
// zoom is in progress)
|
// We're starting a pan, so add this back as a
|
||||||
function updateMarqueeBox() {
|
// state on the stack; it will get replaced
|
||||||
// Express this as a box in the draw object, which
|
// during the pan.
|
||||||
// is passed to an mct-chart in the template for rendering.
|
this.panZoomStack.pushPanZoom(
|
||||||
draw.boxes = marqueeStart ?
|
this.panStartBounds.origin,
|
||||||
[{
|
this.panStartBounds.dimensions
|
||||||
start: toDisplayable(mousePositionToDomainRange(marqueeStart)),
|
);
|
||||||
end: toDisplayable(mousePositionToDomainRange(mousePosition)),
|
$event.preventDefault();
|
||||||
color: [1, 1, 1, 0.5 ]
|
} else {
|
||||||
}] : undefined;
|
// Start marquee zooming
|
||||||
}
|
this.marqueeStart = this.mousePosition;
|
||||||
|
this.updateMarqueeBox();
|
||||||
// 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.
|
|
||||||
draw.dimensions = panZoom.dimensions;
|
|
||||||
draw.origin = [
|
|
||||||
panZoom.origin[0] - domainOffset,
|
|
||||||
panZoom.origin[1]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update tick marks in scope.
|
|
||||||
function updateTicks() {
|
|
||||||
var tickGenerator = new PlotTickGenerator(panZoomStack, formatter);
|
|
||||||
|
|
||||||
domainTicks =
|
|
||||||
tickGenerator.generateDomainTicks(DOMAIN_TICKS);
|
|
||||||
rangeTicks =
|
|
||||||
tickGenerator.generateRangeTicks(RANGE_TICKS);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePan() {
|
|
||||||
var start, current, delta, nextOrigin;
|
|
||||||
|
|
||||||
// Clear the previous panning pan-zoom state
|
|
||||||
panZoomStack.popPanZoom();
|
|
||||||
|
|
||||||
// Calculate what the new resulting pan-zoom should be
|
|
||||||
start = mousePositionToDomainRange(panStart);
|
|
||||||
current = mousePositionToDomainRange(mousePosition);
|
|
||||||
delta = [ current[0] - start[0], current[1] - start[1] ];
|
|
||||||
nextOrigin = [
|
|
||||||
panStartBounds.origin[0] - delta[0],
|
|
||||||
panStartBounds.origin[1] - delta[1]
|
|
||||||
];
|
|
||||||
|
|
||||||
// ...and push a new one at the current mouse position
|
|
||||||
panZoomStack.pushPanZoom(nextOrigin, panStartBounds.dimensions);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete a marquee zoom action.
|
||||||
|
* @param $event the mouse event
|
||||||
|
*/
|
||||||
|
SubPlot.prototype.endDrag = function ($event) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
// Perform a marquee zoom.
|
// Perform a marquee zoom.
|
||||||
function marqueeZoom(start, end) {
|
function marqueeZoom(start, end) {
|
||||||
// Determine what boundary is described by the marquee,
|
// Determine what boundary is described by the marquee,
|
||||||
// in domain-range values. Use the minima for origin, so that
|
// in domain-range values. Use the minima for origin, so that
|
||||||
// it doesn't matter what direction the user marqueed in.
|
// it doesn't matter what direction the user marqueed in.
|
||||||
var a = mousePositionToDomainRange(start),
|
var a = self.mousePositionToDomainRange(start),
|
||||||
b = mousePositionToDomainRange(end),
|
b = self.mousePositionToDomainRange(end),
|
||||||
origin = [
|
origin = [
|
||||||
Math.min(a[0], b[0]),
|
Math.min(a[0], b[0]),
|
||||||
Math.min(a[1], b[1])
|
Math.min(a[1], b[1])
|
||||||
@ -197,196 +334,68 @@ define(
|
|||||||
// Proceed with zoom if zoom dimensions are non zeros
|
// Proceed with zoom if zoom dimensions are non zeros
|
||||||
if (!(dimensions[0] === 0 && dimensions[1] === 0)) {
|
if (!(dimensions[0] === 0 && dimensions[1] === 0)) {
|
||||||
// Push the new state onto the pan-zoom stack
|
// Push the new state onto the pan-zoom stack
|
||||||
panZoomStack.pushPanZoom(origin, dimensions);
|
self.panZoomStack.pushPanZoom(origin, dimensions);
|
||||||
|
|
||||||
// Make sure tick marks reflect new bounds
|
// Make sure tick marks reflect new bounds
|
||||||
updateTicks();
|
self.updateTicks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start with the right initial drawing bounds,
|
this.mousePosition = this.toMousePosition($event);
|
||||||
// tick marks
|
this.subPlotBounds = undefined;
|
||||||
updateDrawingBounds();
|
if (this.marqueeStart) {
|
||||||
updateTicks();
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
/**
|
||||||
/**
|
* Update the drawing bounds, marquee box, and
|
||||||
* Get the set of domain objects which are being
|
* tick marks for this subplot.
|
||||||
* represented in this sub-plot.
|
*/
|
||||||
* @returns {DomainObject[]} the domain objects which
|
SubPlot.prototype.update = function () {
|
||||||
* will have data plotted in this sub-plot
|
this.updateDrawingBounds();
|
||||||
* @memberof platform/features/plot.SubPlot#
|
this.updateMarqueeBox();
|
||||||
*/
|
this.updateTicks();
|
||||||
getTelemetryObjects: function () {
|
};
|
||||||
return telemetryObjects;
|
|
||||||
},
|
/**
|
||||||
/**
|
* Set the domain offset associated with this sub-plot.
|
||||||
* Get ticks mark information appropriate for using in the
|
* A domain offset is subtracted from all domain
|
||||||
* template for this sub-plot's domain axis, as prepared
|
* before lines are drawn to avoid artifacts associated
|
||||||
* by the PlotTickGenerator.
|
* with the use of 32-bit floats when domain values
|
||||||
* @returns {Array} tick marks for the domain axis
|
* are often timestamps (due to insufficient precision.)
|
||||||
* @memberof platform/features/plot.SubPlot#
|
* A SubPlot will be drawing boxes (for marquee zoom) in
|
||||||
*/
|
* the same offset coordinate space, so it needs to know
|
||||||
getDomainTicks: function () {
|
* the value of this to position that marquee box
|
||||||
return domainTicks;
|
* correctly.
|
||||||
},
|
* @param {number} value the domain offset
|
||||||
/**
|
*/
|
||||||
* Get ticks mark information appropriate for using in the
|
SubPlot.prototype.setDomainOffset = function (value) {
|
||||||
* template for this sub-plot's range axis, as prepared
|
this.domainOffset = value;
|
||||||
* by the PlotTickGenerator.
|
};
|
||||||
* @returns {Array} tick marks for the range axis
|
|
||||||
* @memberof platform/features/plot.SubPlot#
|
/**
|
||||||
*/
|
* When used with no argument, check whether or not the user
|
||||||
getRangeTicks: function () {
|
* is currently hovering over this subplot. When used with
|
||||||
return rangeTicks;
|
* an argument, set that state.
|
||||||
},
|
* @param {boolean} [state] the new hovering state
|
||||||
/**
|
* @returns {boolean} the hovering state
|
||||||
* Get the drawing object associated with this sub-plot;
|
*/
|
||||||
* this object will be passed to the mct-chart in which
|
SubPlot.prototype.isHovering = function (state) {
|
||||||
* this sub-plot's lines will be plotted, as its "draw"
|
if (state !== undefined) {
|
||||||
* attribute, and should have the same internal format
|
this.hovering = state;
|
||||||
* expected by that directive.
|
}
|
||||||
* @return {object} the drawing object
|
return this.hovering;
|
||||||
* @memberof platform/features/plot.SubPlot#
|
};
|
||||||
*/
|
|
||||||
getDrawingObject: function () {
|
|
||||||
return 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
|
|
||||||
* @memberof platform/features/plot.SubPlot#
|
|
||||||
*/
|
|
||||||
getHoverCoordinates: function () {
|
|
||||||
return hoverCoordinates;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Handle mouse movement over the chart area.
|
|
||||||
* @param $event the mouse event
|
|
||||||
* @memberof platform/features/plot.SubPlot#
|
|
||||||
*/
|
|
||||||
hover: function ($event) {
|
|
||||||
isHovering = true;
|
|
||||||
subPlotBounds = $event.target.getBoundingClientRect();
|
|
||||||
mousePosition = toMousePosition($event);
|
|
||||||
updateHoverCoordinates();
|
|
||||||
if (marqueeStart) {
|
|
||||||
updateMarqueeBox();
|
|
||||||
}
|
|
||||||
if (panStart) {
|
|
||||||
updatePan();
|
|
||||||
updateDrawingBounds();
|
|
||||||
updateTicks();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Continue a previously-start pan or zoom gesture.
|
|
||||||
* @param $event the mouse event
|
|
||||||
* @memberof platform/features/plot.SubPlot#
|
|
||||||
*/
|
|
||||||
continueDrag: function ($event) {
|
|
||||||
mousePosition = toMousePosition($event);
|
|
||||||
if (marqueeStart) {
|
|
||||||
updateMarqueeBox();
|
|
||||||
}
|
|
||||||
if (panStart) {
|
|
||||||
updatePan();
|
|
||||||
updateDrawingBounds();
|
|
||||||
updateTicks();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Initiate a marquee zoom action.
|
|
||||||
* @param $event the mouse event
|
|
||||||
* @memberof platform/features/plot.SubPlot#
|
|
||||||
*/
|
|
||||||
startDrag: function ($event) {
|
|
||||||
subPlotBounds = $event.target.getBoundingClientRect();
|
|
||||||
mousePosition = toMousePosition($event);
|
|
||||||
// Treat any modifier key as a pan
|
|
||||||
if ($event.altKey || $event.shiftKey || $event.ctrlKey) {
|
|
||||||
// Start panning
|
|
||||||
panStart = mousePosition;
|
|
||||||
panStartBounds = panZoomStack.getPanZoom();
|
|
||||||
// We're starting a pan, so add this back as a
|
|
||||||
// state on the stack; it will get replaced
|
|
||||||
// during the pan.
|
|
||||||
panZoomStack.pushPanZoom(
|
|
||||||
panStartBounds.origin,
|
|
||||||
panStartBounds.dimensions
|
|
||||||
);
|
|
||||||
$event.preventDefault();
|
|
||||||
} else {
|
|
||||||
// Start marquee zooming
|
|
||||||
marqueeStart = mousePosition;
|
|
||||||
updateMarqueeBox();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Complete a marquee zoom action.
|
|
||||||
* @param $event the mouse event
|
|
||||||
* @memberof platform/features/plot.SubPlot#
|
|
||||||
*/
|
|
||||||
endDrag: function ($event) {
|
|
||||||
mousePosition = toMousePosition($event);
|
|
||||||
subPlotBounds = undefined;
|
|
||||||
if (marqueeStart) {
|
|
||||||
marqueeZoom(marqueeStart, mousePosition);
|
|
||||||
marqueeStart = undefined;
|
|
||||||
updateMarqueeBox();
|
|
||||||
updateDrawingBounds();
|
|
||||||
updateTicks();
|
|
||||||
}
|
|
||||||
if (panStart) {
|
|
||||||
// End panning
|
|
||||||
panStart = undefined;
|
|
||||||
panStartBounds = undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Update the drawing bounds, marquee box, and
|
|
||||||
* tick marks for this subplot.
|
|
||||||
* @memberof platform/features/plot.SubPlot#
|
|
||||||
*/
|
|
||||||
update: function () {
|
|
||||||
updateDrawingBounds();
|
|
||||||
updateMarqueeBox();
|
|
||||||
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
|
|
||||||
* @memberof platform/features/plot.SubPlot#
|
|
||||||
*/
|
|
||||||
setDomainOffset: function (value) {
|
|
||||||
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
|
|
||||||
* @memberof platform/features/plot.SubPlot#
|
|
||||||
*/
|
|
||||||
isHovering: function (state) {
|
|
||||||
if (state !== undefined) {
|
|
||||||
isHovering = state;
|
|
||||||
}
|
|
||||||
return isHovering;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return SubPlot;
|
return SubPlot;
|
||||||
|
|
||||||
|
@ -35,28 +35,26 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function SubPlotFactory(telemetryFormatter) {
|
function SubPlotFactory(telemetryFormatter) {
|
||||||
return {
|
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
|
|
||||||
* @memberof SubPlotFactory
|
|
||||||
* @memberof platform/features/plot.SubPlotFactory#
|
|
||||||
*/
|
|
||||||
createSubPlot: function (telemetryObjects, panZoomStack) {
|
|
||||||
return new SubPlot(
|
|
||||||
telemetryObjects,
|
|
||||||
panZoomStack,
|
|
||||||
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;
|
return SubPlotFactory;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,15 +30,53 @@ define(
|
|||||||
key: "stacked",
|
key: "stacked",
|
||||||
name: "Stacked",
|
name: "Stacked",
|
||||||
glyph: "m",
|
glyph: "m",
|
||||||
factory: PlotStackMode
|
Constructor: PlotStackMode
|
||||||
},
|
},
|
||||||
OVERLAID = {
|
OVERLAID = {
|
||||||
key: "overlaid",
|
key: "overlaid",
|
||||||
name: "Overlaid",
|
name: "Overlaid",
|
||||||
glyph: "6",
|
glyph: "6",
|
||||||
factory: PlotOverlayMode
|
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)
|
* Determines which plotting modes (stacked/overlaid)
|
||||||
* are applicable in a given plot view, maintains current
|
* are applicable in a given plot view, maintains current
|
||||||
@ -46,73 +84,74 @@ define(
|
|||||||
* different behaviors associated with these modes.
|
* different behaviors associated with these modes.
|
||||||
* @memberof platform/features/plot
|
* @memberof platform/features/plot
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {DomainObject[]} the telemetry objects being
|
* @param {DomainObject[]} telemetryObjects the telemetry objects being
|
||||||
* represented in this plot view
|
* represented in this plot view
|
||||||
|
* @param {platform/features/plot.SubPlotFactory} subPlotFactory a
|
||||||
|
* factory for creating sub-plots
|
||||||
*/
|
*/
|
||||||
function PlotModeOptions(telemetryObjects, subPlotFactory) {
|
function PlotModeOptions(telemetryObjects, subPlotFactory) {
|
||||||
var options = telemetryObjects.length > 1 ?
|
this.options = telemetryObjects.length > 1 ?
|
||||||
[ OVERLAID, STACKED ] : [ OVERLAID ],
|
[ OVERLAID, STACKED ] : [ OVERLAID ];
|
||||||
mode = options[0], // Initial selection (overlaid)
|
this.mode = this.options[0]; // Initial selection (overlaid)
|
||||||
modeHandler;
|
this.telemetryObjects = telemetryObjects;
|
||||||
|
this.subPlotFactory = subPlotFactory;
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @memberof platform/features/plot.PlotModeOptions#
|
|
||||||
*/
|
|
||||||
getModeHandler: function () {
|
|
||||||
// Lazily initialize
|
|
||||||
if (!modeHandler) {
|
|
||||||
modeHandler = mode.factory(
|
|
||||||
telemetryObjects,
|
|
||||||
subPlotFactory
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return modeHandler;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Get all mode options available for each plot. Each
|
|
||||||
* mode contains a `name` and `glyph` field suitable
|
|
||||||
* for display in a template.
|
|
||||||
* @return {Array} the available modes
|
|
||||||
* @memberof platform/features/plot.PlotModeOptions#
|
|
||||||
*/
|
|
||||||
getModeOptions: function () {
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Get the plotting mode option currently in use.
|
|
||||||
* This will be one of the elements returned from
|
|
||||||
* `getModeOptions`.
|
|
||||||
* @return {object} the current mode
|
|
||||||
* @memberof platform/features/plot.PlotModeOptions#
|
|
||||||
*/
|
|
||||||
getMode: function () {
|
|
||||||
return 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`
|
|
||||||
* @memberof platform/features/plot.PlotModeOptions#
|
|
||||||
*/
|
|
||||||
setMode: function (option) {
|
|
||||||
if (mode !== option) {
|
|
||||||
mode = option;
|
|
||||||
// Clear the existing mode handler, so it
|
|
||||||
// can be instantiated next time it's needed.
|
|
||||||
modeHandler = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 `glyph` 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;
|
return PlotModeOptions;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -31,81 +31,59 @@ define(
|
|||||||
* is one sub-plot which contains all plotted objects.
|
* is one sub-plot which contains all plotted objects.
|
||||||
* @memberof platform/features/plot
|
* @memberof platform/features/plot
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {platform/features/plot.PlotModeHandler}
|
||||||
* @param {DomainObject[]} the domain objects to be plotted
|
* @param {DomainObject[]} the domain objects to be plotted
|
||||||
*/
|
*/
|
||||||
function PlotOverlayMode(telemetryObjects, subPlotFactory) {
|
function PlotOverlayMode(telemetryObjects, subPlotFactory) {
|
||||||
var domainOffset,
|
this.panZoomStack = new PlotPanZoomStack([], []);
|
||||||
panZoomStack = new PlotPanZoomStack([], []),
|
this.subplot = subPlotFactory.createSubPlot(
|
||||||
subplot = subPlotFactory.createSubPlot(
|
telemetryObjects,
|
||||||
telemetryObjects,
|
this.panZoomStack
|
||||||
panZoomStack
|
);
|
||||||
),
|
this.subplots = [ this.subplot ];
|
||||||
subplots = [ subplot ];
|
}
|
||||||
|
|
||||||
function plotTelemetry(prepared) {
|
PlotOverlayMode.prototype.plotTelemetry = function (updater) {
|
||||||
// Fit to the boundaries of the data, but don't
|
// Fit to the boundaries of the data, but don't
|
||||||
// override any user-initiated pan-zoom changes.
|
// override any user-initiated pan-zoom changes.
|
||||||
panZoomStack.setBasePanZoom(
|
this.panZoomStack.setBasePanZoom(
|
||||||
prepared.getOrigin(),
|
updater.getOrigin(),
|
||||||
prepared.getDimensions()
|
updater.getDimensions()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Track the domain offset, used to bias domain values
|
// Track the domain offset, used to bias domain values
|
||||||
// to minimize loss of precision when converted to 32-bit
|
// to minimize loss of precision when converted to 32-bit
|
||||||
// floating point values for display.
|
// floating point values for display.
|
||||||
subplot.setDomainOffset(prepared.getDomainOffset());
|
this.subplot.setDomainOffset(updater.getDomainOffset());
|
||||||
|
|
||||||
// Draw the buffers. Select color by index.
|
// Draw the buffers. Select color by index.
|
||||||
subplot.getDrawingObject().lines = prepared.getLineBuffers().map(function (buf, i) {
|
this.subplot.getDrawingObject().lines =
|
||||||
|
updater.getLineBuffers().map(function (buf, i) {
|
||||||
return {
|
return {
|
||||||
buffer: buf.getBuffer(),
|
buffer: buf.getBuffer(),
|
||||||
color: PlotPalette.getFloatColor(i),
|
color: PlotPalette.getFloatColor(i),
|
||||||
points: buf.getLength()
|
points: buf.getLength()
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
PlotOverlayMode.prototype.getSubPlots = function () {
|
||||||
/**
|
return this.subplots;
|
||||||
* Plot telemetry to the sub-plot(s) managed by this mode.
|
};
|
||||||
* @param {PlotPreparer} prepared the prepared data to plot
|
|
||||||
* @memberof platform/features/plot.PlotOverlayMode#
|
PlotOverlayMode.prototype.isZoomed = function () {
|
||||||
*/
|
return this.panZoomStack.getDepth() > 1;
|
||||||
plotTelemetry: plotTelemetry,
|
};
|
||||||
/**
|
|
||||||
* Get all sub-plots to be displayed in this mode; used
|
PlotOverlayMode.prototype.stepBackPanZoom = function () {
|
||||||
* to populate the plot template.
|
this.panZoomStack.popPanZoom();
|
||||||
* @return {SubPlot[]} all sub-plots to display in this mode
|
this.subplot.update();
|
||||||
* @memberof platform/features/plot.PlotOverlayMode#
|
};
|
||||||
*/
|
|
||||||
getSubPlots: function () {
|
PlotOverlayMode.prototype.unzoom = function () {
|
||||||
return subplots;
|
this.panZoomStack.clearPanZoom();
|
||||||
},
|
this.subplot.update();
|
||||||
/**
|
};
|
||||||
* 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
|
|
||||||
* @memberof platform/features/plot.PlotOverlayMode#
|
|
||||||
*/
|
|
||||||
isZoomed: function () {
|
|
||||||
return panZoomStack.getDepth() > 1;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Undo the most recent pan/zoom change and restore
|
|
||||||
* the prior state.
|
|
||||||
* @memberof platform/features/plot.PlotOverlayMode#
|
|
||||||
*/
|
|
||||||
stepBackPanZoom: function () {
|
|
||||||
panZoomStack.popPanZoom();
|
|
||||||
subplot.update();
|
|
||||||
},
|
|
||||||
unzoom: function () {
|
|
||||||
panZoomStack.clearPanZoom();
|
|
||||||
subplot.update();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return PlotOverlayMode;
|
return PlotOverlayMode;
|
||||||
}
|
}
|
||||||
|
@ -31,99 +31,76 @@ define(
|
|||||||
* is one sub-plot for each plotted object.
|
* is one sub-plot for each plotted object.
|
||||||
* @memberof platform/features/plot
|
* @memberof platform/features/plot
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {platform/features/plot.PlotModeHandler}
|
||||||
* @param {DomainObject[]} the domain objects to be plotted
|
* @param {DomainObject[]} the domain objects to be plotted
|
||||||
*/
|
*/
|
||||||
function PlotStackMode(telemetryObjects, subPlotFactory) {
|
function PlotStackMode(telemetryObjects, subPlotFactory) {
|
||||||
var domainOffset,
|
var self = this;
|
||||||
panZoomStackGroup =
|
|
||||||
new PlotPanZoomStackGroup(telemetryObjects.length),
|
this.panZoomStackGroup =
|
||||||
subplots = telemetryObjects.map(function (telemetryObject, i) {
|
new PlotPanZoomStackGroup(telemetryObjects.length);
|
||||||
|
|
||||||
|
this.subplots = telemetryObjects.map(function (telemetryObject, i) {
|
||||||
return subPlotFactory.createSubPlot(
|
return subPlotFactory.createSubPlot(
|
||||||
[telemetryObject],
|
[telemetryObject],
|
||||||
panZoomStackGroup.getPanZoomStack(i)
|
self.panZoomStackGroup.getPanZoomStack(i)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function plotTelemetryTo(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()
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
function plotTelemetry(prepared) {
|
|
||||||
// Fit to the boundaries of the data, but don't
|
|
||||||
// override any user-initiated pan-zoom changes.
|
|
||||||
panZoomStackGroup.setBasePanZoom(
|
|
||||||
prepared.getOrigin(),
|
|
||||||
prepared.getDimensions()
|
|
||||||
);
|
|
||||||
|
|
||||||
subplots.forEach(function (subplot, index) {
|
|
||||||
plotTelemetryTo(subplot, prepared, index);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Plot telemetry to the sub-plot(s) managed by this mode.
|
|
||||||
* @param {PlotPreparer} prepared the prepared data to plot
|
|
||||||
* @memberof platform/features/plot.PlotStackMode#
|
|
||||||
*/
|
|
||||||
plotTelemetry: plotTelemetry,
|
|
||||||
/**
|
|
||||||
* Get all sub-plots to be displayed in this mode; used
|
|
||||||
* to populate the plot template.
|
|
||||||
* @return {SubPlot[]} all sub-plots to display in this mode
|
|
||||||
* @memberof platform/features/plot.PlotStackMode#
|
|
||||||
*/
|
|
||||||
getSubPlots: function () {
|
|
||||||
return subplots;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @memberof platform/features/plot.PlotStackMode#
|
|
||||||
*/
|
|
||||||
isZoomed: function () {
|
|
||||||
return panZoomStackGroup.getDepth() > 1;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Undo the most recent pan/zoom change and restore
|
|
||||||
* the prior state.
|
|
||||||
* @memberof platform/features/plot.PlotStackMode#
|
|
||||||
*/
|
|
||||||
stepBackPanZoom: function () {
|
|
||||||
panZoomStackGroup.popPanZoom();
|
|
||||||
subplots.forEach(function (subplot) {
|
|
||||||
subplot.update();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Undo all pan/zoom changes and restore the initial state.
|
|
||||||
* @memberof platform/features/plot.PlotStackMode#
|
|
||||||
*/
|
|
||||||
unzoom: function () {
|
|
||||||
panZoomStackGroup.clearPanZoom();
|
|
||||||
subplots.forEach(function (subplot) {
|
|
||||||
subplot.update();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return PlotStackMode;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -28,39 +28,38 @@ define(
|
|||||||
/**
|
/**
|
||||||
* Policy preventing the Plot view from being made available for
|
* Policy preventing the Plot view from being made available for
|
||||||
* domain objects which have non-numeric telemetry.
|
* domain objects which have non-numeric telemetry.
|
||||||
* @implements {Policy}
|
* @implements {Policy.<View, DomainObject>}
|
||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/features/plot
|
* @memberof platform/features/plot
|
||||||
*/
|
*/
|
||||||
function PlotViewPolicy() {
|
function PlotViewPolicy() {
|
||||||
function hasImageTelemetry(domainObject) {
|
}
|
||||||
var telemetry = domainObject &&
|
|
||||||
domainObject.getCapability('telemetry'),
|
|
||||||
metadata = telemetry ? telemetry.getMetadata() : {},
|
|
||||||
ranges = metadata.ranges || [];
|
|
||||||
|
|
||||||
// Generally, we want to allow Plot for telemetry-providing
|
function hasNumericTelemetry(domainObject) {
|
||||||
// objects (most telemetry is plottable.) We only want to
|
var telemetry = domainObject &&
|
||||||
// suppress this for telemetry which only has explicitly
|
domainObject.getCapability('telemetry'),
|
||||||
// non-numeric values.
|
metadata = telemetry ? telemetry.getMetadata() : {},
|
||||||
return ranges.length === 0 || ranges.some(function (range) {
|
ranges = metadata.ranges || [];
|
||||||
|
|
||||||
|
// Generally, we want to allow Plot for telemetry-providing
|
||||||
|
// objects (most telemetry is plottable.) We only want to
|
||||||
|
// suppress this for telemetry which only has explicitly
|
||||||
|
// non-numeric values.
|
||||||
|
return ranges.length === 0 || ranges.some(function (range) {
|
||||||
// Assume format is numeric if it is undefined
|
// Assume format is numeric if it is undefined
|
||||||
// (numeric telemetry is the common case)
|
// (numeric telemetry is the common case)
|
||||||
return range.format === undefined ||
|
return range.format === undefined ||
|
||||||
range.format === 'number';
|
range.format === 'number';
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
PlotViewPolicy.prototype.allow = function (view, domainObject) {
|
||||||
|
if (view.key === 'plot') {
|
||||||
|
return hasNumericTelemetry(domainObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return true;
|
||||||
allow: function (view, domainObject) {
|
};
|
||||||
if (view.key === 'plot') {
|
|
||||||
return hasImageTelemetry(domainObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return PlotViewPolicy;
|
return PlotViewPolicy;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,12 @@ define(
|
|||||||
mockSeries,
|
mockSeries,
|
||||||
controller;
|
controller;
|
||||||
|
|
||||||
|
function bind(method, thisObj) {
|
||||||
|
return function () {
|
||||||
|
return method.apply(thisObj, arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockScope = jasmine.createSpyObj(
|
mockScope = jasmine.createSpyObj(
|
||||||
@ -196,13 +202,13 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("allows plots to be updated", function () {
|
it("allows plots to be updated", function () {
|
||||||
expect(controller.update).not.toThrow();
|
expect(bind(controller.update, controller)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows changing pan-zoom state", function () {
|
it("allows changing pan-zoom state", function () {
|
||||||
expect(controller.isZoomed).not.toThrow();
|
expect(bind(controller.isZoomed, controller)).not.toThrow();
|
||||||
expect(controller.stepBackPanZoom).not.toThrow();
|
expect(bind(controller.stepBackPanZoom, controller)).not.toThrow();
|
||||||
expect(controller.unzoom).not.toThrow();
|
expect(bind(controller.unzoom, controller)).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("indicates if a request is pending", function () {
|
it("indicates if a request is pending", function () {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user