mirror of
https://github.com/nasa/openmct.git
synced 2025-05-22 18:23:59 +00:00
271 lines
10 KiB
JavaScript
271 lines
10 KiB
JavaScript
/*****************************************************************************
|
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
|
* as represented by the Administrator of the National Aeronautics and Space
|
|
* Administration. All rights reserved.
|
|
*
|
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
* Open MCT Web includes source code licensed under additional open source
|
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
* this source code distribution or the Licensing information page available
|
|
* at runtime from the About dialog for additional information.
|
|
*****************************************************************************/
|
|
/*global define,Float32Array*/
|
|
|
|
define(
|
|
[],
|
|
function () {
|
|
"use strict";
|
|
|
|
/**
|
|
* Contains the buffer used to draw a plot.
|
|
* @param {number} domainOffset number to subtract from domain values
|
|
* @param {number} initialSize initial buffer size
|
|
* @param {number} maxSize maximum buffer size
|
|
* @memberof platform/features/plot
|
|
* @constructor
|
|
*/
|
|
function PlotLineBuffer(domainOffset, initialSize, maxSize) {
|
|
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;
|
|
}
|
|
);
|
|
|