From 18bc7d3637e8e1ca3c4532484666893991cffda4 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 12 Aug 2015 14:49:03 -0700 Subject: [PATCH] [Code Style] Begin using prototypes in Plot bundle WTD-1482 --- .../features/plot/src/elements/PlotAxis.js | 36 +- .../plot/src/elements/PlotLimitTracker.js | 50 +- .../features/plot/src/elements/PlotLine.js | 110 +++-- .../plot/src/elements/PlotLineBuffer.js | 455 +++++++++--------- .../plot/src/elements/PlotPanZoomStack.js | 208 ++++---- 5 files changed, 419 insertions(+), 440 deletions(-) diff --git a/platform/features/plot/src/elements/PlotAxis.js b/platform/features/plot/src/elements/PlotAxis.js index 2066b9c1bd..25795fd347 100644 --- a/platform/features/plot/src/elements/PlotAxis.js +++ b/platform/features/plot/src/elements/PlotAxis.js @@ -62,26 +62,22 @@ define( (metadatas || []).forEach(buildOptionsForMetadata); - // Plot axis will be used directly from the Angular - // template, so expose properties directly to facilitate - // two-way data binding (for drop-down menus) - return { - /** - * The set of options applicable for this axis; - * an array of objects, where each object contains a - * "key" field and a "name" field (for machine- and - * human-readable names respectively) - * @memberof platform/features/plot.PlotAxis# - */ - 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 - }; + /** + * The currently chosen option for this axis. An + * initial value is provided; this will be updated + * directly form the plot template. + * @memberof platform/features/plot.PlotAxis# + */ + this.active = options[0] || defaultValue; + + /** + * The set of options applicable for this axis; + * an array of objects, where each object contains a + * "key" field and a "name" field (for machine- and + * human-readable names respectively) + * @memberof platform/features/plot.PlotAxis# + */ + this.options = options; } return PlotAxis; diff --git a/platform/features/plot/src/elements/PlotLimitTracker.js b/platform/features/plot/src/elements/PlotLimitTracker.js index 9993ff2144..9309c50f32 100644 --- a/platform/features/plot/src/elements/PlotLimitTracker.js +++ b/platform/features/plot/src/elements/PlotLimitTracker.js @@ -21,26 +21,32 @@ *****************************************************************************/ /*global define,Float32Array*/ -/** - * Prepares data to be rendered in a GL Plot. Handles - * the conversion from data API to displayable buffers. - */ define( [], function () { 'use strict'; - 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 * @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 */ 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) { var limit = telemetryObject.getCapability('limit'), @@ -52,17 +58,21 @@ define( } } - return { - update: function () { - legendClasses = {}; - handle.getTelemetryObjects().forEach(updateLimit); - }, - getLegendClass: function (domainObject) { - var id = domainObject && domainObject.getId(); - return id && legendClasses[id]; - } - }; - } + handle.getTelemetryObjects().forEach(updateLimit); + + this.legendClasses = legendClasses; + }; + + /** + * Get the CSS class associated with any limit violations for this + * telemetry object. + * @param {DomainObject} domainObject the telemetry object to check + * @returns {string} the CSS class name, if any + */ + PlotLimitTracker.prototype.getLegendClass = function (domainObject) { + var id = domainObject && domainObject.getId(); + return id && this.legendClasses[id]; + }; return PlotLimitTracker; diff --git a/platform/features/plot/src/elements/PlotLine.js b/platform/features/plot/src/elements/PlotLine.js index ae9b3b56b3..abb23c8770 100644 --- a/platform/features/plot/src/elements/PlotLine.js +++ b/platform/features/plot/src/elements/PlotLine.js @@ -33,6 +33,47 @@ define( * @constructor */ function PlotLine(buffer) { + this.buffer = buffer; + } + + /** + * Add a point to this plot line. + * @param {number} domainValue the domain value + * @param {number} rangeValue the range value + */ + PlotLine.prototype.addPoint = function (domainValue, rangeValue) { + var buffer = this.buffer, + index; + + // Make sure we got real/useful values here... + if (domainValue !== undefined && rangeValue !== undefined) { + index = buffer.findInsertionIndex(domainValue); + + // Already in the buffer? Skip insertion + if (index < 0) { + return; + } + + // Insert the point + if (!buffer.insertPoint(domainValue, rangeValue, index)) { + // If insertion failed, trim from the beginning... + buffer.trim(1); + // ...and try again. + buffer.insertPoint(domainValue, rangeValue, index); + } + } + }; + + /** + * Add a series of telemetry data to this plot line. + * @param {TelemetrySeries} series the data series + * @param {string} [domain] the key indicating which domain + * to use when looking up data from this series + * @param {string} [range] the key indicating which range + * to use when looking up data from this series + */ + PlotLine.prototype.addSeries = function (series, domain, range) { + var buffer = this.buffer; // Insert a time-windowed data series into the buffer function insertSeriesWindow(seriesWindow) { @@ -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 - // clear insertion point is available; - // if not, should split and add each half. - // Insertion operation also needs to factor out - // redundant timestamps, for overlapping data - insertSeriesWindow(createWindow(series, domain, range)); - } - }; - } + // Should try to add via insertion if a + // clear insertion point is available; + // if not, should split and add each half. + // Insertion operation also needs to factor out + // redundant timestamps, for overlapping data + insertSeriesWindow(new PlotSeriesWindow( + series, + domain, + range, + 0, + series.getPointCount() + )); + }; return PlotLine; } diff --git a/platform/features/plot/src/elements/PlotLineBuffer.js b/platform/features/plot/src/elements/PlotLineBuffer.js index 5cf52ad49d..af0bbe553b 100644 --- a/platform/features/plot/src/elements/PlotLineBuffer.js +++ b/platform/features/plot/src/elements/PlotLineBuffer.js @@ -35,236 +35,235 @@ define( * @constructor */ function PlotLineBuffer(domainOffset, initialSize, maxSize) { - var buffer = new Float32Array(initialSize * 2), - rangeExtrema = [ Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY ], - length = 0; - - // Binary search for an insertion index - function binSearch(value, min, max) { - var mid = Math.floor((min + max) / 2), - found = buffer[mid * 2]; - - // On collisions, insert at same index - if (found === value) { - return mid; - } - - // Otherwise, if we're down to a single index, - // we've found our insertion point - if (min >= max) { - // Compare the found timestamp with the search - // value to decide if we'll insert after or before. - return min + ((found < value) ? 1 : 0); - } - - // Finally, do the recursive step - if (found < value) { - return binSearch(value, mid + 1, max); - } else { - return binSearch(value, min, mid - 1); - } - } - - // Increase the size of the buffer - function doubleBufferSize() { - var sz = Math.min(maxSize * 2, buffer.length * 2), - canDouble = sz > buffer.length, - doubled = canDouble && new Float32Array(sz); - - if (canDouble) { - doubled.set(buffer); // Copy contents of original - buffer = doubled; - } - - return canDouble; - } - - // Decrease the size of the buffer - function halveBufferSize() { - var sz = Math.max(initialSize * 2, buffer.length / 2), - canHalve = sz < buffer.length; - - if (canHalve) { - buffer = new Float32Array(buffer.subarray(0, sz)); - } - - return canHalve; - } - - // Set a value in the buffer - function setValue(index, domainValue, rangeValue) { - buffer[index * 2] = domainValue - domainOffset; - buffer[index * 2 + 1] = rangeValue; - // Track min/max of range values (min/max for - // domain values can be read directly from buffer) - rangeExtrema[0] = Math.min(rangeExtrema[0], rangeValue); - rangeExtrema[1] = Math.max(rangeExtrema[1], rangeValue); - } - - return { - /** - * Get the WebGL-displayable buffer of points to plot. - * @returns {Float32Array} displayable buffer for this line - * @memberof platform/features/plot.PlotLineBuffer# - */ - getBuffer: function () { - return buffer; - }, - /** - * Get the number of points stored in this buffer. - * @returns {number} the number of points stored - * @memberof platform/features/plot.PlotLineBuffer# - */ - getLength: function () { - return length; - }, - /** - * Get the min/max range values that are currently in this - * buffer. Unlike range extrema, these will change as the - * buffer gets trimmed. - * @returns {number[]} min, max domain values - * @memberof platform/features/plot.PlotLineBuffer# - */ - getDomainExtrema: function () { - // Since these are ordered in the buffer, assume - // these are the values at the first and last index - return [ - buffer[0] + domainOffset, - buffer[length * 2 - 2] + domainOffset - ]; - }, - /** - * Get the min/max range values that have been observed for this - * buffer. Note that these values may have been trimmed out at - * some point. - * @returns {number[]} min, max range values - * @memberof platform/features/plot.PlotLineBuffer# - */ - getRangeExtrema: function () { - return rangeExtrema; - }, - /** - * Remove values from this buffer. - * Normally, values are removed from the start - * of the buffer; a truthy value in the second argument - * will cause values to be removed from the end. - * @param {number} count number of values to remove - * @param {boolean} [fromEnd] true if the most recent - * values should be removed - * @memberof platform/features/plot.PlotLineBuffer# - */ - trim: function (count, fromEnd) { - // If we're removing values from the start... - if (!fromEnd) { - // ...do so by shifting buffer contents over - buffer.set(buffer.subarray(2 * count)); - } - // Reduce used buffer size accordingly - length -= count; - // Finally, if less than half of the buffer is being - // used, free up some memory. - if (length < buffer.length / 4) { - halveBufferSize(); - } - }, - /** - * Insert data from the provided series at the specified - * index. If this would exceed the buffer's maximum capacity, - * this operation fails and the buffer is unchanged. - * @param {TelemetrySeries} series the series to insert - * @param {number} index the index at which to insert this - * series - * @returns {boolean} true if insertion succeeded; otherwise - * false - * @memberof platform/features/plot.PlotLineBuffer# - */ - insert: function (series, index) { - var sz = series.getPointCount(), - i; - - // Don't allow append after the end; that doesn't make sense - index = Math.min(index, length); - - // Resize if necessary - while (sz > ((buffer.length / 2) - length)) { - if (!doubleBufferSize()) { - // Can't make room for this, insertion fails - return false; - } - } - - // Shift data over if necessary - if (index < length) { - buffer.set( - buffer.subarray(index * 2, length * 2), - (index + sz) * 2 - ); - } - - // Insert data into the set - for (i = 0; i < sz; i += 1) { - setValue( - i + index, - series.getDomainValue(i), - series.getRangeValue(i) - ); - } - - // Increase the length - length += sz; - - // Indicate that insertion was successful - return true; - }, - /** - * Append a single data point. - * @memberof platform/features/plot.PlotLineBuffer# - */ - insertPoint: function (domainValue, rangeValue, index) { - // Don't allow - index = Math.min(length, index); - - // Ensure there is space for this point - if (length >= (buffer.length / 2)) { - if (!doubleBufferSize()) { - return false; - } - } - - // Put the data in the buffer - setValue(length, domainValue, rangeValue); - - // Update length - length += 1; - - // Indicate that this was successful - return true; - }, - /** - * Find an index for inserting data with this - * timestamp. The second argument indicates whether - * we are searching for insert-before or insert-after - * positions. - * Timestamps are meant to be unique, so if a collision - * occurs, this will return -1. - * @param {number} timestamp timestamp to insert - * @returns {number} the index for insertion (or -1) - * @memberof platform/features/plot.PlotLineBuffer# - */ - findInsertionIndex: function (timestamp) { - var value = timestamp - domainOffset; - - // Handle empty buffer case and check for an - // append opportunity (which is most common case for - // real-time data so is optimized-for) before falling - // back to a binary search for the insertion point. - return (length < 1) ? 0 : - (value > buffer[length * 2 - 2]) ? length : - binSearch(value, 0, length - 1); - } - }; + this.buffer = new Float32Array(initialSize * 2); + this.rangeExtrema = + [ Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY ]; + this.length = 0; + this.domainOffset = domainOffset; + this.initialSize = initialSize; + this.maxSize = maxSize; } + // Binary search for an insertion index + PlotLineBuffer.prototype.binSearch = function (value, min, max) { + var mid = Math.floor((min + max) / 2), + found = this.buffer[mid * 2]; + + // On collisions, insert at same index + if (found === value) { + return mid; + } + + // Otherwise, if we're down to a single index, + // we've found our insertion point + if (min >= max) { + // Compare the found timestamp with the search + // value to decide if we'll insert after or before. + return min + ((found < value) ? 1 : 0); + } + + // Finally, do the recursive step + if (found < value) { + return this.binSearch(value, mid + 1, max); + } else { + return this.binSearch(value, min, mid - 1); + } + }; + + // Increase the size of the buffer + PlotLineBuffer.prototype.doubleBufferSize = function () { + var sz = Math.min(this.maxSize * 2, this.buffer.length * 2), + canDouble = sz > this.buffer.length, + doubled = canDouble && new Float32Array(sz); + + if (canDouble) { + doubled.set(this.buffer); // Copy contents of original + this.buffer = doubled; + } + + return canDouble; + }; + + // Decrease the size of the buffer + PlotLineBuffer.prototype.halveBufferSize = function () { + var sz = Math.max(this.initialSize * 2, this.buffer.length / 2), + canHalve = sz < this.buffer.length; + + if (canHalve) { + this.buffer = new Float32Array(this.buffer.subarray(0, sz)); + } + + return canHalve; + }; + + // Set a value in the buffer + PlotLineBuffer.prototype.setValue = function (index, domainValue, rangeValue) { + this.buffer[index * 2] = domainValue - this.domainOffset; + this.buffer[index * 2 + 1] = rangeValue; + // Track min/max of range values (min/max for + // domain values can be read directly from buffer) + this.rangeExtrema[0] = Math.min(this.rangeExtrema[0], rangeValue); + this.rangeExtrema[1] = Math.max(this.rangeExtrema[1], rangeValue); + }; + + /** + * Get the WebGL-displayable buffer of points to plot. + * @returns {Float32Array} displayable buffer for this line + */ + PlotLineBuffer.prototype.getBuffer = function () { + return this.buffer; + }; + + /** + * Get the number of points stored in this buffer. + * @returns {number} the number of points stored + */ + PlotLineBuffer.prototype.getLength = function () { + return this.length; + }; + + /** + * Get the min/max range values that are currently in this + * buffer. Unlike range extrema, these will change as the + * buffer gets trimmed. + * @returns {number[]} min, max domain values + */ + PlotLineBuffer.prototype.getDomainExtrema = function () { + // Since these are ordered in the buffer, assume + // these are the values at the first and last index + return [ + this.buffer[0] + this.domainOffset, + this.buffer[this.length * 2 - 2] + this.domainOffset + ]; + }; + + /** + * Get the min/max range values that have been observed for this + * buffer. Note that these values may have been trimmed out at + * some point. + * @returns {number[]} min, max range values + */ + PlotLineBuffer.prototype.getRangeExtrema = function () { + return this.rangeExtrema; + }; + + /** + * Remove values from this buffer. + * Normally, values are removed from the start + * of the buffer; a truthy value in the second argument + * will cause values to be removed from the end. + * @param {number} count number of values to remove + * @param {boolean} [fromEnd] true if the most recent + * values should be removed + */ + PlotLineBuffer.prototype.trim = function (count, fromEnd) { + // If we're removing values from the start... + if (!fromEnd) { + // ...do so by shifting buffer contents over + this.buffer.set(this.buffer.subarray(2 * count)); + } + // Reduce used buffer size accordingly + this.length -= count; + // Finally, if less than half of the buffer is being + // used, free up some memory. + if (this.length < this.buffer.length / 4) { + this.halveBufferSize(); + } + }; + + /** + * Insert data from the provided series at the specified + * index. If this would exceed the buffer's maximum capacity, + * this operation fails and the buffer is unchanged. + * @param {TelemetrySeries} series the series to insert + * @param {number} index the index at which to insert this + * series + * @returns {boolean} true if insertion succeeded; otherwise + * false + */ + PlotLineBuffer.prototype.insert = function (series, index) { + var sz = series.getPointCount(), + i; + + // Don't allow append after the end; that doesn't make sense + index = Math.min(index, this.length); + + // Resize if necessary + while (sz > ((this.buffer.length / 2) - this.length)) { + if (!this.doubleBufferSize()) { + // Can't make room for this, insertion fails + return false; + } + } + + // Shift data over if necessary + if (index < this.length) { + this.buffer.set( + this.buffer.subarray(index * 2, this.length * 2), + (index + sz) * 2 + ); + } + + // Insert data into the set + for (i = 0; i < sz; i += 1) { + this.setValue( + i + index, + series.getDomainValue(i), + series.getRangeValue(i) + ); + } + + // Increase the length + this.length += sz; + + // Indicate that insertion was successful + return true; + }; + + /** + * Append a single data point. + * @memberof platform/features/plot.PlotLineBuffer# + */ + PlotLineBuffer.prototype.insertPoint = function (domainValue, rangeValue) { + // Ensure there is space for this point + if (this.length >= (this.buffer.length / 2)) { + if (!this.doubleBufferSize()) { + return false; + } + } + + // Put the data in the buffer + this.setValue(this.length, domainValue, rangeValue); + + // Update length + this.length += 1; + + // Indicate that this was successful + return true; + }; + + /** + * Find an index for inserting data with this + * timestamp. The second argument indicates whether + * we are searching for insert-before or insert-after + * positions. + * Timestamps are meant to be unique, so if a collision + * occurs, this will return -1. + * @param {number} timestamp timestamp to insert + * @returns {number} the index for insertion (or -1) + */ + PlotLineBuffer.prototype.findInsertionIndex = function (timestamp) { + var value = timestamp - this.domainOffset; + + // Handle empty buffer case and check for an + // append opportunity (which is most common case for + // real-time data so is optimized-for) before falling + // back to a binary search for the insertion point. + return (this.length < 1) ? 0 : + (value > this.buffer[this.length * 2 - 2]) ? this.length : + this.binSearch(value, 0, this.length - 1); + }; + return PlotLineBuffer; } ); diff --git a/platform/features/plot/src/elements/PlotPanZoomStack.js b/platform/features/plot/src/elements/PlotPanZoomStack.js index 66e3593488..d31d53c128 100644 --- a/platform/features/plot/src/elements/PlotPanZoomStack.js +++ b/platform/features/plot/src/elements/PlotPanZoomStack.js @@ -44,124 +44,100 @@ define( */ function PlotPanZoomStack(origin, dimensions) { // Use constructor parameters as the stack's initial state - var stack = [{ origin: origin, dimensions: dimensions }]; - - // Various functions which follow are simply wrappers for - // normal stack-like array methods, with the exception that - // they prevent undesired modification and enforce that this - // stack must remain non-empty. - // See JSDoc for specific methods below for more detail. - function getDepth() { - return stack.length; - } - - 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 - * of items on the stack. A depth of one means that no - * panning or zooming relative to the base value has - * been applied. - * @returns {number} the depth of the stack - * @memberof platform/features/plot.PlotPanZoomStack# - */ - getDepth: getDepth, - - /** - * Push a new pan-zoom state onto the stack; this will - * become the active pan-zoom state. - * @param {number[]} origin the new origin - * @param {number[]} dimensions the new dimensions - * @memberof platform/features/plot.PlotPanZoomStack# - */ - pushPanZoom: pushPanZoom, - - /** - * Pop a pan-zoom state from the stack. Whatever pan-zoom - * state was previously present will become current. - * If called when there is only one pan-zoom state on the - * stack, this acts as a no-op (that is, the lowest - * pan-zoom state on the stack cannot be popped, to ensure - * that some pan-zoom state is always available.) - * @memberof platform/features/plot.PlotPanZoomStack# - */ - popPanZoom: popPanZoom, - - /** - * Set the base pan-zoom state; that is, the state at the - * bottom of the stack. This allows the "unzoomed" state of - * a plot to be updated (e.g. as new data comes in) without - * interfering with the user's chosen zoom level. - * @param {number[]} origin the base origin - * @param {number[]} dimensions the base dimensions - * @memberof platform/features/plot.PlotPanZoomStack# - */ - setBasePanZoom: setBasePanZoom, - - /** - * Clear the pan-zoom stack down to its bottom element; - * in effect, pop all elements but the last, e.g. to remove - * any temporary user modifications to pan-zoom state. - * @memberof platform/features/plot.PlotPanZoomStack# - */ - clearPanZoom: clearPanZoom, - - /** - * Get the current pan-zoom state (the state at the top - * of the stack), expressed as an object with "origin" and - * "dimensions" fields. - * @returns {object} the current pan-zoom state - * @memberof platform/features/plot.PlotPanZoomStack# - */ - getPanZoom: getPanZoom, - - /** - * Get the current origin, as represented on the top of the - * stack. - * @returns {number[]} the current plot origin - * @memberof platform/features/plot.PlotPanZoomStack# - */ - getOrigin: getOrigin, - - /** - * Get the current dimensions, as represented on the top of - * the stack. - * @returns {number[]} the current plot dimensions - * @memberof platform/features/plot.PlotPanZoomStack# - */ - getDimensions: getDimensions - }; + this.stack = [{ origin: origin, dimensions: dimensions }]; } + // Various functions which follow are simply wrappers for + // normal stack-like array methods, with the exception that + // they prevent undesired modification and enforce that this + // stack must remain non-empty. + // See JSDoc for specific methods below for more detail. + + /** + * Get the current stack depth; that is, the number + * of items on the stack. A depth of one means that no + * panning or zooming relative to the base value has + * been applied. + * @returns {number} the depth of the stack + */ + PlotPanZoomStack.prototype.getDepth = function getDepth() { + return this.stack.length; + }; + + /** + * Push a new pan-zoom state onto the stack; this will + * become the active pan-zoom state. + * @param {number[]} origin the new origin + * @param {number[]} dimensions the new dimensions + */ + PlotPanZoomStack.prototype.pushPanZoom = function (origin, dimensions) { + this.stack.push({ origin: origin, dimensions: dimensions }); + }; + + /** + * Pop a pan-zoom state from the stack. Whatever pan-zoom + * state was previously present will become current. + * If called when there is only one pan-zoom state on the + * stack, this acts as a no-op (that is, the lowest + * pan-zoom state on the stack cannot be popped, to ensure + * that some pan-zoom state is always available.) + */ + PlotPanZoomStack.prototype.popPanZoom = function popPanZoom() { + if (stack.length > 1) { + this.stack.pop(); + } + }; + + /** + * Set the base pan-zoom state; that is, the state at the + * bottom of the stack. This allows the "unzoomed" state of + * a plot to be updated (e.g. as new data comes in) without + * interfering with the user's chosen zoom level. + * @param {number[]} origin the base origin + * @param {number[]} dimensions the base dimensions + * @memberof platform/features/plot.PlotPanZoomStack# + */ + PlotPanZoomStack.prototype.setBasePanZoom = function (origin, dimensions) { + this.stack[0] = { origin: origin, dimensions: dimensions }; + }; + + /** + * Clear the pan-zoom stack down to its bottom element; + * in effect, pop all elements but the last, e.g. to remove + * any temporary user modifications to pan-zoom state. + */ + PlotPanZoomStack.prototype.clearPanZoom = function clearPanZoom() { + this.stack = [this.stack[0]]; + }; + + /** + * Get the current pan-zoom state (the state at the top + * of the stack), expressed as an object with "origin" and + * "dimensions" fields. + * @returns {object} the current pan-zoom state + */ + PlotPanZoomStack.prototype.getPanZoom = function getPanZoom() { + return this.stack[this.stack.length - 1]; + }; + + /** + * Get the current origin, as represented on the top of the + * stack. + * @returns {number[]} the current plot origin + */ + PlotPanZoomStack.prototype.getOrigin = function getOrigin() { + return this.getPanZoom().origin; + }; + + /** + * Get the current dimensions, as represented on the top of + * the stack. + * @returns {number[]} the current plot dimensions + */ + PlotPanZoomStack.prototype.getDimensions = function getDimensions() { + return this.getPanZoom().dimensions; + }; + return PlotPanZoomStack; } );