[Code Style] Begin using prototypes in Plot bundle

WTD-1482
This commit is contained in:
Victor Woeltjen
2015-08-12 14:49:03 -07:00
parent 175490e1f7
commit 18bc7d3637
5 changed files with 419 additions and 440 deletions

View File

@ -62,10 +62,14 @@ define(
(metadatas || []).forEach(buildOptionsForMetadata); (metadatas || []).forEach(buildOptionsForMetadata);
// Plot axis will be used directly from the Angular /**
// template, so expose properties directly to facilitate * The currently chosen option for this axis. An
// two-way data binding (for drop-down menus) * initial value is provided; this will be updated
return { * directly form the plot template.
* @memberof platform/features/plot.PlotAxis#
*/
this.active = options[0] || defaultValue;
/** /**
* The set of options applicable for this axis; * The set of options applicable for this axis;
* an array of objects, where each object contains a * an array of objects, where each object contains a
@ -73,15 +77,7 @@ define(
* human-readable names respectively) * human-readable names respectively)
* @memberof platform/features/plot.PlotAxis# * @memberof platform/features/plot.PlotAxis#
*/ */
options: options, this.options = options;
/**
* The currently chosen option for this axis. An
* initial value is provided; this will be updated
* directly form the plot template.
* @memberof platform/features/plot.PlotAxis#
*/
active: options[0] || defaultValue
};
} }
return PlotAxis; return PlotAxis;

View File

@ -21,26 +21,32 @@
*****************************************************************************/ *****************************************************************************/
/*global define,Float32Array*/ /*global define,Float32Array*/
/**
* Prepares data to be rendered in a GL Plot. Handles
* the conversion from data API to displayable buffers.
*/
define( define(
[], [],
function () { function () {
'use strict'; 'use strict';
var MAX_POINTS = 86400,
INITIAL_SIZE = 675; // 1/128 of MAX_POINTS
/** /**
* Tracks the limit state of telemetry objects being plotted.
* @memberof platform/features/plot * @memberof platform/features/plot
* @constructor * @constructor
* @param {TelemetryHandle} handle the handle to telemetry access * @param {platform/telemetry.TelemetryHandle} handle the handle
* to telemetry access
* @param {string} range the key to use when looking up range values * @param {string} range the key to use when looking up range values
*/ */
function PlotLimitTracker(handle, range) { function PlotLimitTracker(handle, range) {
var legendClasses = {}; this.handle = handle;
this.range = range;
this.legendClasses = {};
}
/**
* Update limit states to reflect the latest data.
*/
PlotLimitTracker.prototype.update = function () {
var legendClasses = {},
range = this.range,
handle = this.handle;
function updateLimit(telemetryObject) { function updateLimit(telemetryObject) {
var limit = telemetryObject.getCapability('limit'), var limit = telemetryObject.getCapability('limit'),
@ -52,17 +58,21 @@ define(
} }
} }
return {
update: function () {
legendClasses = {};
handle.getTelemetryObjects().forEach(updateLimit); handle.getTelemetryObjects().forEach(updateLimit);
},
getLegendClass: function (domainObject) { this.legendClasses = legendClasses;
var id = domainObject && domainObject.getId(); };
return id && legendClasses[id];
} /**
* Get the CSS class associated with any limit violations for this
* telemetry object.
* @param {DomainObject} domainObject the telemetry object to check
* @returns {string} the CSS class name, if any
*/
PlotLimitTracker.prototype.getLegendClass = function (domainObject) {
var id = domainObject && domainObject.getId();
return id && this.legendClasses[id];
}; };
}
return PlotLimitTracker; return PlotLimitTracker;

View File

@ -33,6 +33,47 @@ define(
* @constructor * @constructor
*/ */
function PlotLine(buffer) { function PlotLine(buffer) {
this.buffer = buffer;
}
/**
* Add a point to this plot line.
* @param {number} domainValue the domain value
* @param {number} rangeValue the range value
*/
PlotLine.prototype.addPoint = function (domainValue, rangeValue) {
var buffer = this.buffer,
index;
// Make sure we got real/useful values here...
if (domainValue !== undefined && rangeValue !== undefined) {
index = buffer.findInsertionIndex(domainValue);
// Already in the buffer? Skip insertion
if (index < 0) {
return;
}
// Insert the point
if (!buffer.insertPoint(domainValue, rangeValue, index)) {
// If insertion failed, trim from the beginning...
buffer.trim(1);
// ...and try again.
buffer.insertPoint(domainValue, rangeValue, index);
}
}
};
/**
* Add a series of telemetry data to this plot line.
* @param {TelemetrySeries} series the data series
* @param {string} [domain] the key indicating which domain
* to use when looking up data from this series
* @param {string} [range] the key indicating which range
* to use when looking up data from this series
*/
PlotLine.prototype.addSeries = function (series, domain, range) {
var buffer = this.buffer;
// Insert a time-windowed data series into the buffer // Insert a time-windowed data series into the buffer
function insertSeriesWindow(seriesWindow) { function insertSeriesWindow(seriesWindow) {
@ -60,62 +101,19 @@ define(
} }
} }
function createWindow(series, domain, range) {
return new PlotSeriesWindow(
series,
domain,
range,
0,
series.getPointCount()
);
}
return {
/**
* Add a point to this plot line.
* @param {number} domainValue the domain value
* @param {number} rangeValue the range value
* @memberof platform/features/plot.PlotLine
*/
addPoint: function (domainValue, rangeValue) {
var index;
// Make sure we got real/useful values here...
if (domainValue !== undefined && rangeValue !== undefined) {
index = buffer.findInsertionIndex(domainValue);
// Already in the buffer? Skip insertion
if (index < 0) {
return;
}
// Insert the point
if (!buffer.insertPoint(domainValue, rangeValue, index)) {
// If insertion failed, trim from the beginning...
buffer.trim(1);
// ...and try again.
buffer.insertPoint(domainValue, rangeValue, index);
}
}
},
/**
* Add a series of telemetry data to this plot line.
* @param {TelemetrySeries} series the data series
* @param {string} [domain] the key indicating which domain
* to use when looking up data from this series
* @param {string} [range] the key indicating which range
* to use when looking up data from this series
* @memberof platform/features/plot.PlotLine
*/
addSeries: function (series, domain, range) {
// Should try to add via insertion if a // Should try to add via insertion if a
// clear insertion point is available; // clear insertion point is available;
// if not, should split and add each half. // if not, should split and add each half.
// Insertion operation also needs to factor out // Insertion operation also needs to factor out
// redundant timestamps, for overlapping data // redundant timestamps, for overlapping data
insertSeriesWindow(createWindow(series, domain, range)); insertSeriesWindow(new PlotSeriesWindow(
} series,
domain,
range,
0,
series.getPointCount()
));
}; };
}
return PlotLine; return PlotLine;
} }

View File

@ -35,14 +35,19 @@ define(
* @constructor * @constructor
*/ */
function PlotLineBuffer(domainOffset, initialSize, maxSize) { function PlotLineBuffer(domainOffset, initialSize, maxSize) {
var buffer = new Float32Array(initialSize * 2), this.buffer = new Float32Array(initialSize * 2);
rangeExtrema = [ Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY ], this.rangeExtrema =
length = 0; [ Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY ];
this.length = 0;
this.domainOffset = domainOffset;
this.initialSize = initialSize;
this.maxSize = maxSize;
}
// Binary search for an insertion index // Binary search for an insertion index
function binSearch(value, min, max) { PlotLineBuffer.prototype.binSearch = function (value, min, max) {
var mid = Math.floor((min + max) / 2), var mid = Math.floor((min + max) / 2),
found = buffer[mid * 2]; found = this.buffer[mid * 2];
// On collisions, insert at same index // On collisions, insert at same index
if (found === value) { if (found === value) {
@ -59,90 +64,89 @@ define(
// Finally, do the recursive step // Finally, do the recursive step
if (found < value) { if (found < value) {
return binSearch(value, mid + 1, max); return this.binSearch(value, mid + 1, max);
} else { } else {
return binSearch(value, min, mid - 1); return this.binSearch(value, min, mid - 1);
}
} }
};
// Increase the size of the buffer // Increase the size of the buffer
function doubleBufferSize() { PlotLineBuffer.prototype.doubleBufferSize = function () {
var sz = Math.min(maxSize * 2, buffer.length * 2), var sz = Math.min(this.maxSize * 2, this.buffer.length * 2),
canDouble = sz > buffer.length, canDouble = sz > this.buffer.length,
doubled = canDouble && new Float32Array(sz); doubled = canDouble && new Float32Array(sz);
if (canDouble) { if (canDouble) {
doubled.set(buffer); // Copy contents of original doubled.set(this.buffer); // Copy contents of original
buffer = doubled; this.buffer = doubled;
} }
return canDouble; return canDouble;
} };
// Decrease the size of the buffer // Decrease the size of the buffer
function halveBufferSize() { PlotLineBuffer.prototype.halveBufferSize = function () {
var sz = Math.max(initialSize * 2, buffer.length / 2), var sz = Math.max(this.initialSize * 2, this.buffer.length / 2),
canHalve = sz < buffer.length; canHalve = sz < this.buffer.length;
if (canHalve) { if (canHalve) {
buffer = new Float32Array(buffer.subarray(0, sz)); this.buffer = new Float32Array(this.buffer.subarray(0, sz));
} }
return canHalve; return canHalve;
} };
// Set a value in the buffer // Set a value in the buffer
function setValue(index, domainValue, rangeValue) { PlotLineBuffer.prototype.setValue = function (index, domainValue, rangeValue) {
buffer[index * 2] = domainValue - domainOffset; this.buffer[index * 2] = domainValue - this.domainOffset;
buffer[index * 2 + 1] = rangeValue; this.buffer[index * 2 + 1] = rangeValue;
// Track min/max of range values (min/max for // Track min/max of range values (min/max for
// domain values can be read directly from buffer) // domain values can be read directly from buffer)
rangeExtrema[0] = Math.min(rangeExtrema[0], rangeValue); this.rangeExtrema[0] = Math.min(this.rangeExtrema[0], rangeValue);
rangeExtrema[1] = Math.max(rangeExtrema[1], rangeValue); this.rangeExtrema[1] = Math.max(this.rangeExtrema[1], rangeValue);
} };
return {
/** /**
* Get the WebGL-displayable buffer of points to plot. * Get the WebGL-displayable buffer of points to plot.
* @returns {Float32Array} displayable buffer for this line * @returns {Float32Array} displayable buffer for this line
* @memberof platform/features/plot.PlotLineBuffer#
*/ */
getBuffer: function () { PlotLineBuffer.prototype.getBuffer = function () {
return buffer; return this.buffer;
}, };
/** /**
* Get the number of points stored in this buffer. * Get the number of points stored in this buffer.
* @returns {number} the number of points stored * @returns {number} the number of points stored
* @memberof platform/features/plot.PlotLineBuffer#
*/ */
getLength: function () { PlotLineBuffer.prototype.getLength = function () {
return length; return this.length;
}, };
/** /**
* Get the min/max range values that are currently in this * Get the min/max range values that are currently in this
* buffer. Unlike range extrema, these will change as the * buffer. Unlike range extrema, these will change as the
* buffer gets trimmed. * buffer gets trimmed.
* @returns {number[]} min, max domain values * @returns {number[]} min, max domain values
* @memberof platform/features/plot.PlotLineBuffer#
*/ */
getDomainExtrema: function () { PlotLineBuffer.prototype.getDomainExtrema = function () {
// Since these are ordered in the buffer, assume // Since these are ordered in the buffer, assume
// these are the values at the first and last index // these are the values at the first and last index
return [ return [
buffer[0] + domainOffset, this.buffer[0] + this.domainOffset,
buffer[length * 2 - 2] + domainOffset this.buffer[this.length * 2 - 2] + this.domainOffset
]; ];
}, };
/** /**
* Get the min/max range values that have been observed for this * Get the min/max range values that have been observed for this
* buffer. Note that these values may have been trimmed out at * buffer. Note that these values may have been trimmed out at
* some point. * some point.
* @returns {number[]} min, max range values * @returns {number[]} min, max range values
* @memberof platform/features/plot.PlotLineBuffer#
*/ */
getRangeExtrema: function () { PlotLineBuffer.prototype.getRangeExtrema = function () {
return rangeExtrema; return this.rangeExtrema;
}, };
/** /**
* Remove values from this buffer. * Remove values from this buffer.
* Normally, values are removed from the start * Normally, values are removed from the start
@ -151,22 +155,22 @@ define(
* @param {number} count number of values to remove * @param {number} count number of values to remove
* @param {boolean} [fromEnd] true if the most recent * @param {boolean} [fromEnd] true if the most recent
* values should be removed * values should be removed
* @memberof platform/features/plot.PlotLineBuffer#
*/ */
trim: function (count, fromEnd) { PlotLineBuffer.prototype.trim = function (count, fromEnd) {
// If we're removing values from the start... // If we're removing values from the start...
if (!fromEnd) { if (!fromEnd) {
// ...do so by shifting buffer contents over // ...do so by shifting buffer contents over
buffer.set(buffer.subarray(2 * count)); this.buffer.set(this.buffer.subarray(2 * count));
} }
// Reduce used buffer size accordingly // Reduce used buffer size accordingly
length -= count; this.length -= count;
// Finally, if less than half of the buffer is being // Finally, if less than half of the buffer is being
// used, free up some memory. // used, free up some memory.
if (length < buffer.length / 4) { if (this.length < this.buffer.length / 4) {
halveBufferSize(); this.halveBufferSize();
} }
}, };
/** /**
* Insert data from the provided series at the specified * Insert data from the provided series at the specified
* index. If this would exceed the buffer's maximum capacity, * index. If this would exceed the buffer's maximum capacity,
@ -176,34 +180,33 @@ define(
* series * series
* @returns {boolean} true if insertion succeeded; otherwise * @returns {boolean} true if insertion succeeded; otherwise
* false * false
* @memberof platform/features/plot.PlotLineBuffer#
*/ */
insert: function (series, index) { PlotLineBuffer.prototype.insert = function (series, index) {
var sz = series.getPointCount(), var sz = series.getPointCount(),
i; i;
// Don't allow append after the end; that doesn't make sense // Don't allow append after the end; that doesn't make sense
index = Math.min(index, length); index = Math.min(index, this.length);
// Resize if necessary // Resize if necessary
while (sz > ((buffer.length / 2) - length)) { while (sz > ((this.buffer.length / 2) - this.length)) {
if (!doubleBufferSize()) { if (!this.doubleBufferSize()) {
// Can't make room for this, insertion fails // Can't make room for this, insertion fails
return false; return false;
} }
} }
// Shift data over if necessary // Shift data over if necessary
if (index < length) { if (index < this.length) {
buffer.set( this.buffer.set(
buffer.subarray(index * 2, length * 2), this.buffer.subarray(index * 2, this.length * 2),
(index + sz) * 2 (index + sz) * 2
); );
} }
// Insert data into the set // Insert data into the set
for (i = 0; i < sz; i += 1) { for (i = 0; i < sz; i += 1) {
setValue( this.setValue(
i + index, i + index,
series.getDomainValue(i), series.getDomainValue(i),
series.getRangeValue(i) series.getRangeValue(i)
@ -211,35 +214,34 @@ define(
} }
// Increase the length // Increase the length
length += sz; this.length += sz;
// Indicate that insertion was successful // Indicate that insertion was successful
return true; return true;
}, };
/** /**
* Append a single data point. * Append a single data point.
* @memberof platform/features/plot.PlotLineBuffer# * @memberof platform/features/plot.PlotLineBuffer#
*/ */
insertPoint: function (domainValue, rangeValue, index) { PlotLineBuffer.prototype.insertPoint = function (domainValue, rangeValue) {
// Don't allow
index = Math.min(length, index);
// Ensure there is space for this point // Ensure there is space for this point
if (length >= (buffer.length / 2)) { if (this.length >= (this.buffer.length / 2)) {
if (!doubleBufferSize()) { if (!this.doubleBufferSize()) {
return false; return false;
} }
} }
// Put the data in the buffer // Put the data in the buffer
setValue(length, domainValue, rangeValue); this.setValue(this.length, domainValue, rangeValue);
// Update length // Update length
length += 1; this.length += 1;
// Indicate that this was successful // Indicate that this was successful
return true; return true;
}, };
/** /**
* Find an index for inserting data with this * Find an index for inserting data with this
* timestamp. The second argument indicates whether * timestamp. The second argument indicates whether
@ -249,21 +251,18 @@ define(
* occurs, this will return -1. * occurs, this will return -1.
* @param {number} timestamp timestamp to insert * @param {number} timestamp timestamp to insert
* @returns {number} the index for insertion (or -1) * @returns {number} the index for insertion (or -1)
* @memberof platform/features/plot.PlotLineBuffer#
*/ */
findInsertionIndex: function (timestamp) { PlotLineBuffer.prototype.findInsertionIndex = function (timestamp) {
var value = timestamp - domainOffset; var value = timestamp - this.domainOffset;
// Handle empty buffer case and check for an // Handle empty buffer case and check for an
// append opportunity (which is most common case for // append opportunity (which is most common case for
// real-time data so is optimized-for) before falling // real-time data so is optimized-for) before falling
// back to a binary search for the insertion point. // back to a binary search for the insertion point.
return (length < 1) ? 0 : return (this.length < 1) ? 0 :
(value > buffer[length * 2 - 2]) ? length : (value > this.buffer[this.length * 2 - 2]) ? this.length :
binSearch(value, 0, length - 1); this.binSearch(value, 0, this.length - 1);
}
}; };
}
return PlotLineBuffer; return PlotLineBuffer;
} }

View File

@ -44,66 +44,35 @@ define(
*/ */
function PlotPanZoomStack(origin, dimensions) { function PlotPanZoomStack(origin, dimensions) {
// Use constructor parameters as the stack's initial state // Use constructor parameters as the stack's initial state
var stack = [{ origin: origin, dimensions: dimensions }]; this.stack = [{ origin: origin, dimensions: dimensions }];
}
// Various functions which follow are simply wrappers for // Various functions which follow are simply wrappers for
// normal stack-like array methods, with the exception that // normal stack-like array methods, with the exception that
// they prevent undesired modification and enforce that this // they prevent undesired modification and enforce that this
// stack must remain non-empty. // stack must remain non-empty.
// See JSDoc for specific methods below for more detail. // See JSDoc for specific methods below for more detail.
function getDepth() {
return stack.length;
}
function pushPanZoom(origin, dimensions) {
stack.push({ origin: origin, dimensions: dimensions });
}
function popPanZoom() {
if (stack.length > 1) {
stack.pop();
}
}
function clearPanZoom() {
stack = [stack[0]];
}
function setBasePanZoom(origin, dimensions) {
stack[0] = { origin: origin, dimensions: dimensions };
}
function getPanZoom() {
return stack[stack.length - 1];
}
function getOrigin() {
return getPanZoom().origin;
}
function getDimensions() {
return getPanZoom().dimensions;
}
return {
/** /**
* Get the current stack depth; that is, the number * Get the current stack depth; that is, the number
* of items on the stack. A depth of one means that no * of items on the stack. A depth of one means that no
* panning or zooming relative to the base value has * panning or zooming relative to the base value has
* been applied. * been applied.
* @returns {number} the depth of the stack * @returns {number} the depth of the stack
* @memberof platform/features/plot.PlotPanZoomStack#
*/ */
getDepth: getDepth, PlotPanZoomStack.prototype.getDepth = function getDepth() {
return this.stack.length;
};
/** /**
* Push a new pan-zoom state onto the stack; this will * Push a new pan-zoom state onto the stack; this will
* become the active pan-zoom state. * become the active pan-zoom state.
* @param {number[]} origin the new origin * @param {number[]} origin the new origin
* @param {number[]} dimensions the new dimensions * @param {number[]} dimensions the new dimensions
* @memberof platform/features/plot.PlotPanZoomStack#
*/ */
pushPanZoom: pushPanZoom, PlotPanZoomStack.prototype.pushPanZoom = function (origin, dimensions) {
this.stack.push({ origin: origin, dimensions: dimensions });
};
/** /**
* Pop a pan-zoom state from the stack. Whatever pan-zoom * Pop a pan-zoom state from the stack. Whatever pan-zoom
@ -112,9 +81,12 @@ define(
* stack, this acts as a no-op (that is, the lowest * stack, this acts as a no-op (that is, the lowest
* pan-zoom state on the stack cannot be popped, to ensure * pan-zoom state on the stack cannot be popped, to ensure
* that some pan-zoom state is always available.) * that some pan-zoom state is always available.)
* @memberof platform/features/plot.PlotPanZoomStack#
*/ */
popPanZoom: popPanZoom, PlotPanZoomStack.prototype.popPanZoom = function popPanZoom() {
if (stack.length > 1) {
this.stack.pop();
}
};
/** /**
* Set the base pan-zoom state; that is, the state at the * Set the base pan-zoom state; that is, the state at the
@ -125,42 +97,46 @@ define(
* @param {number[]} dimensions the base dimensions * @param {number[]} dimensions the base dimensions
* @memberof platform/features/plot.PlotPanZoomStack# * @memberof platform/features/plot.PlotPanZoomStack#
*/ */
setBasePanZoom: setBasePanZoom, PlotPanZoomStack.prototype.setBasePanZoom = function (origin, dimensions) {
this.stack[0] = { origin: origin, dimensions: dimensions };
};
/** /**
* Clear the pan-zoom stack down to its bottom element; * Clear the pan-zoom stack down to its bottom element;
* in effect, pop all elements but the last, e.g. to remove * in effect, pop all elements but the last, e.g. to remove
* any temporary user modifications to pan-zoom state. * any temporary user modifications to pan-zoom state.
* @memberof platform/features/plot.PlotPanZoomStack#
*/ */
clearPanZoom: clearPanZoom, PlotPanZoomStack.prototype.clearPanZoom = function clearPanZoom() {
this.stack = [this.stack[0]];
};
/** /**
* Get the current pan-zoom state (the state at the top * Get the current pan-zoom state (the state at the top
* of the stack), expressed as an object with "origin" and * of the stack), expressed as an object with "origin" and
* "dimensions" fields. * "dimensions" fields.
* @returns {object} the current pan-zoom state * @returns {object} the current pan-zoom state
* @memberof platform/features/plot.PlotPanZoomStack#
*/ */
getPanZoom: getPanZoom, PlotPanZoomStack.prototype.getPanZoom = function getPanZoom() {
return this.stack[this.stack.length - 1];
};
/** /**
* Get the current origin, as represented on the top of the * Get the current origin, as represented on the top of the
* stack. * stack.
* @returns {number[]} the current plot origin * @returns {number[]} the current plot origin
* @memberof platform/features/plot.PlotPanZoomStack#
*/ */
getOrigin: getOrigin, PlotPanZoomStack.prototype.getOrigin = function getOrigin() {
return this.getPanZoom().origin;
};
/** /**
* Get the current dimensions, as represented on the top of * Get the current dimensions, as represented on the top of
* the stack. * the stack.
* @returns {number[]} the current plot dimensions * @returns {number[]} the current plot dimensions
* @memberof platform/features/plot.PlotPanZoomStack#
*/ */
getDimensions: getDimensions PlotPanZoomStack.prototype.getDimensions = function getDimensions() {
return this.getPanZoom().dimensions;
}; };
}
return PlotPanZoomStack; return PlotPanZoomStack;
} }