[Code Style] Continue refactor of Plot bundle

Continue refactoring Plot bundle to use prototypes,
WTD-1482
This commit is contained in:
Victor Woeltjen 2015-08-12 16:12:44 -07:00
parent 18bc7d3637
commit aefad6fdd3
7 changed files with 531 additions and 557 deletions

View File

@ -83,7 +83,7 @@ define(
* that some pan-zoom state is always available.) * that some pan-zoom state is always available.)
*/ */
PlotPanZoomStack.prototype.popPanZoom = function popPanZoom() { PlotPanZoomStack.prototype.popPanZoom = function popPanZoom() {
if (stack.length > 1) { if (this.stack.length > 1) {
this.stack.pop(); this.stack.pop();
} }
}; };

View File

@ -38,15 +38,13 @@ define(
* group * group
*/ */
function PlotPanZoomStackGroup(count) { function PlotPanZoomStackGroup(count) {
var stacks = [], var self = this;
decoratedStacks = [],
i;
// Push a pan-zoom state; the index argument identifies // Push a pan-zoom state; the index argument identifies
// which stack originated the request (all other stacks // which stack originated the request (all other stacks
// will ignore the range part of the change.) // will ignore the range part of the change.)
function pushPanZoom(origin, dimensions, index) { function pushPanZoom(origin, dimensions, index) {
stacks.forEach(function (stack, i) { self.stacks.forEach(function (stack, i) {
if (i === index) { if (i === index) {
// Do a normal push for the specified stack // Do a normal push for the specified stack
stack.pushPanZoom(origin, dimensions); stack.pushPanZoom(origin, dimensions);
@ -61,26 +59,6 @@ define(
}); });
} }
// Pop one pan-zoom state from all stacks
function popPanZoom() {
stacks.forEach(function (stack) {
stack.popPanZoom();
});
}
// Set the base pan-zoom state for all stacks
function setBasePanZoom(origin, dimensions) {
stacks.forEach(function (stack) {
stack.setBasePanZoom(origin, dimensions);
});
}
// Clear the pan-zoom state of all stacks
function clearPanZoom() {
stacks.forEach(function (stack) {
stack.clearPanZoom();
});
}
// Decorate a pan-zoom stack; returns an object with // Decorate a pan-zoom stack; returns an object with
// the same interface, but whose stack-mutation methods // the same interface, but whose stack-mutation methods
@ -92,30 +70,40 @@ define(
result.pushPanZoom = function (origin, dimensions) { result.pushPanZoom = function (origin, dimensions) {
pushPanZoom(origin, dimensions, index); pushPanZoom(origin, dimensions, index);
}; };
result.setBasePanZoom = setBasePanZoom; result.setBasePanZoom = function () {
result.popPanZoom = popPanZoom; self.setBasePanZoom.apply(self, arguments);
result.clearPanZoom = clearPanZoom; };
result.popPanZoom = function () {
self.popPanZoom.apply(self, arguments);
};
result.clearPanZoom = function () {
self.clearPanZoom.apply(self, arguments);
};
return result; return result;
} }
// Create the stacks in this group ... // Create the stacks in this group ...
while (stacks.length < count) { this.stacks = [];
stacks.push(new PlotPanZoomStack([], [])); while (this.stacks.length < count) {
this.stacks.push(new PlotPanZoomStack([], []));
} }
// ... and their decorated-to-synchronize versions. // ... and their decorated-to-synchronize versions.
decoratedStacks = stacks.map(decorateStack); this.decoratedStacks = this.stacks.map(decorateStack);
}
return {
/** /**
* Pop a pan-zoom state from all stacks in the group. * Pop a pan-zoom state from all stacks in the group.
* If called when there is only one pan-zoom state on each * If called when there is only one pan-zoom state on each
* 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.PlotPanZoomStackGroup#
*/ */
popPanZoom: popPanZoom, PlotPanZoomStackGroup.prototype.popPanZoom = function () {
this.stacks.forEach(function (stack) {
stack.popPanZoom();
});
};
/** /**
* Set the base pan-zoom state for all stacks in this group. * Set the base pan-zoom state for all stacks in this group.
@ -125,18 +113,25 @@ define(
* interfering with the user's chosen pan/zoom states. * interfering with the user's chosen pan/zoom states.
* @param {number[]} origin the base origin * @param {number[]} origin the base origin
* @param {number[]} dimensions the base dimensions * @param {number[]} dimensions the base dimensions
* @memberof platform/features/plot.PlotPanZoomStackGroup#
*/ */
setBasePanZoom: setBasePanZoom, PlotPanZoomStackGroup.prototype.setBasePanZoom = function (origin, dimensions) {
this.stacks.forEach(function (stack) {
stack.setBasePanZoom(origin, dimensions);
});
};
/** /**
* Clear all pan-zoom stacks in this group down to * Clear all pan-zoom stacks in this group down to
* their bottom element; in effect, pop all elements * their bottom element; in effect, pop all elements
* but the last, e.g. to remove any temporary user * but the last, e.g. to remove any temporary user
* modifications to pan-zoom state. * modifications to pan-zoom state.
* @memberof platform/features/plot.PlotPanZoomStackGroup#
*/ */
clearPanZoom: clearPanZoom, PlotPanZoomStackGroup.prototype.clearPanZoom = function () {
this.stacks.forEach(function (stack) {
stack.clearPanZoom();
});
};
/** /**
* Get the current stack depth; that is, the number * Get the current stack depth; that is, the number
* of items on each stack in the group. * of items on each stack in the group.
@ -144,14 +139,14 @@ define(
* 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 stacks in this group * @returns {number} the depth of the stacks in this group
* @memberof platform/features/plot.PlotPanZoomStackGroup#
*/ */
getDepth: function () { PlotPanZoomStackGroup.prototype.getDepth = function () {
// All stacks are kept in sync, so look up depth // All stacks are kept in sync, so look up depth
// from the first one. // from the first one.
return stacks.length > 0 ? return this.stacks.length > 0 ?
stacks[0].getDepth() : 0; this.stacks[0].getDepth() : 0;
}, };
/** /**
* Get a specific pan-zoom stack in this group. * Get a specific pan-zoom stack in this group.
* Stacks are specified by index; this index must be less * Stacks are specified by index; this index must be less
@ -165,15 +160,11 @@ define(
* @param {number} index the index of the stack to get * @param {number} index the index of the stack to get
* @returns {PlotPanZoomStack} the pan-zoom stack in the * @returns {PlotPanZoomStack} the pan-zoom stack in the
* group identified by that index * group identified by that index
* @memberof platform/features/plot.PlotPanZoomStackGroup#
*/ */
getPanZoomStack: function (index) { PlotPanZoomStackGroup.prototype.getPanZoomStack = function (index) {
return decoratedStacks[index]; return this.decoratedStacks[index];
}
}; };
}
return PlotPanZoomStackGroup; return PlotPanZoomStackGroup;
} }
); );

View File

@ -48,8 +48,7 @@ define(
function PlotPosition(x, y, width, height, panZoomStack) { function PlotPosition(x, y, width, height, panZoomStack) {
var panZoom = panZoomStack.getPanZoom(), var panZoom = panZoomStack.getPanZoom(),
origin = panZoom.origin, origin = panZoom.origin,
dimensions = panZoom.dimensions, dimensions = panZoom.dimensions;
position;
function convert(v, i) { function convert(v, i) {
return v * dimensions[i] + origin[i]; return v * dimensions[i] + origin[i];
@ -57,45 +56,42 @@ define(
if (!dimensions || !origin) { if (!dimensions || !origin) {
// We need both dimensions and origin to compute a position // We need both dimensions and origin to compute a position
position = []; this.position = [];
} else { } else {
// Convert from pixel to domain-range space. // Convert from pixel to domain-range space.
// Note that range is reversed from the y-axis in pixel space // Note that range is reversed from the y-axis in pixel space
//(positive range points up, positive pixel-y points down) //(positive range points up, positive pixel-y points down)
position = [ x / width, (height - y) / height ].map(convert); this.position =
[ x / width, (height - y) / height ].map(convert);
}
} }
return {
/** /**
* Get the domain value corresponding to this pixel position. * Get the domain value corresponding to this pixel position.
* @returns {number} the domain value * @returns {number} the domain value
* @memberof platform/features/plot.PlotPosition#
*/ */
getDomain: function () { PlotPosition.prototype.getDomain = function () {
return position[0]; return this.position[0];
}, };
/** /**
* Get the range value corresponding to this pixel position. * Get the range value corresponding to this pixel position.
* @returns {number} the range value * @returns {number} the range value
* @memberof platform/features/plot.PlotPosition#
*/ */
getRange: function () { PlotPosition.prototype.getRange =function () {
return position[1]; return this.position[1];
}, };
/** /**
* Get the domain and values corresponding to this * Get the domain and values corresponding to this
* pixel position. * pixel position.
* @returns {number[]} an array containing the domain and * @returns {number[]} an array containing the domain and
* the range value, in that order * the range value, in that order
* @memberof platform/features/plot.PlotPosition#
*/ */
getPosition: function () { PlotPosition.prototype.getPosition = function () {
return position; return this.position;
}
}; };
}
return PlotPosition; return PlotPosition;
} }
); );

View File

@ -49,8 +49,7 @@ define(
min = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY], min = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
x, x,
y, y,
domainOffset = Number.POSITIVE_INFINITY, domainOffset = Number.POSITIVE_INFINITY;
buffers;
// Remove any undefined data sets // Remove any undefined data sets
datas = (datas || []).filter(identity); datas = (datas || []).filter(identity);
@ -85,41 +84,48 @@ define(
} }
// Convert to Float32Array // Convert to Float32Array
buffers = vertices.map(function (v) { return new Float32Array(v); }); this.buffers = vertices.map(function (v) {
return new Float32Array(v);
});
this.min = min;
this.max = max;
this.domainOffset = domainOffset;
}
return {
/** /**
* Get the dimensions which bound all data in the provided * Get the dimensions which bound all data in the provided
* data sets. This is given as a two-element array where the * data sets. This is given as a two-element array where the
* first element is domain, and second is range. * first element is domain, and second is range.
* @returns {number[]} the dimensions which bound this data set * @returns {number[]} the dimensions which bound this data set
* @memberof platform/features/plot.PlotPreparer#
*/ */
getDimensions: function () { PlotPreparer.prototype.getDimensions = function () {
var max = this.max, min = this.min;
return [max[0] - min[0], max[1] - min[1]]; return [max[0] - min[0], max[1] - min[1]];
}, };
/** /**
* Get the origin of this data set's boundary. * Get the origin of this data set's boundary.
* This is given as a two-element array where the * This is given as a two-element array where the
* first element is domain, and second is range. * first element is domain, and second is range.
* The domain value here is not adjusted by the domain offset. * The domain value here is not adjusted by the domain offset.
* @returns {number[]} the origin of this data set's boundary * @returns {number[]} the origin of this data set's boundary
* @memberof platform/features/plot.PlotPreparer#
*/ */
getOrigin: function () { PlotPreparer.prototype.getOrigin = function () {
return min; return this.min;
}, };
/** /**
* Get the domain offset; this offset will have been subtracted * Get the domain offset; this offset will have been subtracted
* from all domain values in all buffers returned by this * from all domain values in all buffers returned by this
* preparer, in order to minimize loss-of-precision due to * preparer, in order to minimize loss-of-precision due to
* conversion to the 32-bit float format needed by WebGL. * conversion to the 32-bit float format needed by WebGL.
* @returns {number} the domain offset * @returns {number} the domain offset
* @memberof platform/features/plot.PlotPreparer#
*/ */
getDomainOffset: function () { PlotPreparer.prototype.getDomainOffset = function () {
return domainOffset; return this.domainOffset;
}, };
/** /**
* Get all renderable buffers for this data set. This will * Get all renderable buffers for this data set. This will
* be returned as an array which can be correlated back to * be returned as an array which can be correlated back to
@ -136,13 +142,10 @@ define(
* cause aliasing artifacts (particularly for timestamps) * cause aliasing artifacts (particularly for timestamps)
* *
* @returns {Float32Array[]} the buffers for these traces * @returns {Float32Array[]} the buffers for these traces
* @memberof platform/features/plot.PlotPreparer#
*/ */
getBuffers: function () { PlotPreparer.prototype.getBuffers = function () {
return buffers; return this.buffers;
}
}; };
}
return PlotPreparer; return PlotPreparer;

View File

@ -30,40 +30,52 @@ define(
* insertion into a plot line. * insertion into a plot line.
* @constructor * @constructor
* @memberof platform/features/plot * @memberof platform/features/plot
* @implements {TelemetrySeries}
*/ */
function PlotSeriesWindow(series, domain, range, start, end) { function PlotSeriesWindow(series, domain, range, start, end) {
return { this.series = series;
getPointCount: function () { this.domain = domain;
return end - start; this.range = range;
}, this.start = start;
getDomainValue: function (index) { this.end = end;
return series.getDomainValue(index + start, domain); }
},
getRangeValue: function (index) { PlotSeriesWindow.prototype.getPointCount = function () {
return series.getRangeValue(index + start, range); return this.end - this.start;
}, };
split: function () {
var mid = Math.floor((end + start) / 2); PlotSeriesWindow.prototype.getDomainValue = function (index) {
return ((end - start) > 1) ? return this.series.getDomainValue(index + this.start, this.domain);
};
PlotSeriesWindow.prototype.getRangeValue = function (index) {
return this.series.getRangeValue(index + this.start, this.range);
};
/**
* Split this series into two series of equal (or nearly-equal) size.
* @returns {PlotSeriesWindow[]} two series
*/
PlotSeriesWindow.prototype.split = function () {
var mid = Math.floor((this.end + this.start) / 2);
return ((this.end - this.start) > 1) ?
[ [
new PlotSeriesWindow( new PlotSeriesWindow(
series, this.series,
domain, this.domain,
range, this.range,
start, this.start,
mid mid
), ),
new PlotSeriesWindow( new PlotSeriesWindow(
series, this.series,
domain, this.domain,
range, this.range,
mid, mid,
end this.end
) )
] : []; ] : [];
}
}; };
}
return PlotSeriesWindow; return PlotSeriesWindow;
} }

View File

@ -39,11 +39,14 @@ define(
* domain and range values. * domain and range values.
*/ */
function PlotTickGenerator(panZoomStack, formatter) { function PlotTickGenerator(panZoomStack, formatter) {
this.panZoomStack = panZoomStack;
this.formatter = formatter;
}
// Generate ticks; interpolate from start up to // Generate ticks; interpolate from start up to
// start + span in count steps, using the provided // start + span in count steps, using the provided
// formatter to represent each value. // formatter to represent each value.
function generateTicks(start, span, count, format) { PlotTickGenerator.prototype.generateTicks = function (start, span, count, format) {
var step = span / (count - 1), var step = span / (count - 1),
result = [], result = [],
i; i;
@ -55,45 +58,38 @@ define(
} }
return result; return result;
} };
return {
/** /**
* Generate tick marks for the domain axis. * Generate tick marks for the domain axis.
* @param {number} count the number of ticks * @param {number} count the number of ticks
* @returns {string[]} labels for those ticks * @returns {string[]} labels for those ticks
* @memberof platform/features/plot.PlotTickGenerator#
*/ */
generateDomainTicks: function (count) { PlotTickGenerator.prototype.generateDomainTicks = function (count) {
var panZoom = panZoomStack.getPanZoom(); var panZoom = this.panZoomStack.getPanZoom();
return generateTicks( return this.generateTicks(
panZoom.origin[0], panZoom.origin[0],
panZoom.dimensions[0], panZoom.dimensions[0],
count, count,
formatter.formatDomainValue this.formatter.formatDomainValue
); );
}, };
/** /**
* Generate tick marks for the range axis. * Generate tick marks for the range axis.
* @param {number} count the number of ticks * @param {number} count the number of ticks
* @returns {string[]} labels for those ticks * @returns {string[]} labels for those ticks
* @memberof platform/features/plot.PlotTickGenerator#
*/ */
generateRangeTicks: function (count) { PlotTickGenerator.prototype.generateRangeTicks = function (count) {
var panZoom = panZoomStack.getPanZoom(); var panZoom = this.panZoomStack.getPanZoom();
return generateTicks( return this.generateTicks(
panZoom.origin[1], panZoom.origin[1],
panZoom.dimensions[1], panZoom.dimensions[1],
count, count,
formatter.formatRangeValue this.formatter.formatRangeValue
); );
}
}; };
}
return PlotTickGenerator; return PlotTickGenerator;
} }
); );

View File

@ -21,10 +21,6 @@
*****************************************************************************/ *****************************************************************************/
/*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(
['./PlotLine', './PlotLineBuffer'], ['./PlotLine', './PlotLineBuffer'],
function (PlotLine, PlotLineBuffer) { function (PlotLine, PlotLineBuffer) {
@ -44,84 +40,37 @@ define(
* @param {TelemetryHandle} handle the handle to telemetry access * @param {TelemetryHandle} handle the handle to telemetry access
* @param {string} domain the key to use when looking up domain values * @param {string} domain the key to use when looking up domain values
* @param {string} range the key to use when looking up range values * @param {string} range the key to use when looking up range values
* @param {number} maxDuration maximum plot duration to display * @param {number} fixedDuration maximum plot duration to display
* @param {number} maxPoints maximum number of points to display * @param {number} maxPoints maximum number of points to display
*/ */
function PlotUpdater(handle, domain, range, fixedDuration, maxPoints) { function PlotUpdater(handle, domain, range, fixedDuration, maxPoints) {
var ids = [], this.handle = handle;
lines = {}, this.domain = domain;
dimensions = [0, 0], this.range = range;
origin = [0, 0], this.fixedDuration = fixedDuration;
domainExtrema, this.maxPoints = maxPoints;
rangeExtrema,
buffers = {}, this.ids = [];
bufferArray = [], this.lines = {};
domainOffset; this.buffers = {};
this.bufferArray = [];
// Use a default MAX_POINTS if none is provided
this.maxPoints = maxPoints !== undefined ? maxPoints : MAX_POINTS;
this.dimensions = [0, 0];
this.origin = [0, 0];
// Initially prepare state for these objects.
// Note that this may be an empty array at this time,
// so we also need to check during update cycles.
this.update();
}
// Look up a domain object's id (for mapping, below) // Look up a domain object's id (for mapping, below)
function getId(domainObject) { function getId(domainObject) {
return domainObject.getId(); return domainObject.getId();
} }
// Check if this set of ids matches the current set of ids
// (used to detect if line preparation can be skipped)
function idsMatch(nextIds) {
return ids.length === nextIds.length &&
nextIds.every(function (id, index) {
return ids[index] === id;
});
}
// Prepare plot lines for this group of telemetry objects
function prepareLines(telemetryObjects) {
var nextIds = telemetryObjects.map(getId),
next = {};
// Detect if we already have everything we need prepared
if (idsMatch(nextIds)) {
// Nothing to prepare, move on
return;
}
// Built up a set of ids. Note that we can only
// create plot lines after our domain offset has
// been determined.
if (domainOffset !== undefined) {
// Update list of ids in use
ids = nextIds;
// Create buffers for these objects
bufferArray = ids.map(function (id) {
buffers[id] = buffers[id] || new PlotLineBuffer(
domainOffset,
INITIAL_SIZE,
maxPoints
);
next[id] = lines[id] || new PlotLine(buffers[id]);
return buffers[id];
});
}
// If there are no more lines, clear the domain offset
if (Object.keys(next).length < 1) {
domainOffset = undefined;
}
// Update to the current set of lines
lines = next;
}
// Initialize the domain offset, based on these observed values
function initializeDomainOffset(values) {
domainOffset =
((domainOffset === undefined) && (values.length > 0)) ?
(values.reduce(function (a, b) {
return (a || 0) + (b || 0);
}, 0) / values.length) :
domainOffset;
}
// Used in the reduce step of updateExtrema // Used in the reduce step of updateExtrema
function reduceExtrema(a, b) { function reduceExtrema(a, b) {
return [ Math.min(a[0], b[0]), Math.max(a[1], b[1]) ]; return [ Math.min(a[0], b[0]), Math.max(a[1], b[1]) ];
@ -137,164 +86,167 @@ define(
return extrema[0]; return extrema[0];
} }
// Expand range slightly so points near edges are visible // Check if this set of ids matches the current set of ids
function expandRange() { // (used to detect if line preparation can be skipped)
var padding = PADDING_RATIO * dimensions[1], PlotUpdater.prototype.idsMatch = function (nextIds) {
top; var ids = this.ids;
padding = Math.max(padding, 1.0); return ids.length === nextIds.length &&
top = Math.ceil(origin[1] + dimensions[1] + padding / 2); nextIds.every(function (id, index) {
origin[1] = Math.floor(origin[1] - padding / 2); return ids[index] === id;
dimensions[1] = top - origin[1]; });
};
// Prepare plot lines for this group of telemetry objects
PlotUpdater.prototype.prepareLines = function (telemetryObjects) {
var nextIds = telemetryObjects.map(getId),
next = {},
self = this;
// Detect if we already have everything we need prepared
if (this.idsMatch(nextIds)) {
// Nothing to prepare, move on
return;
} }
// Built up a set of ids. Note that we can only
// create plot lines after our domain offset has
// been determined.
if (this.domainOffset !== undefined) {
// Update list of ids in use
this.ids = nextIds;
// Create buffers for these objects
this.bufferArray = this.ids.map(function (id) {
self.buffers[id] = self.buffers[id] || new PlotLineBuffer(
self.domainOffset,
INITIAL_SIZE,
self.maxPoints
);
next[id] =
self.lines[id] || new PlotLine(self.buffers[id]);
return self.buffers[id];
});
}
// If there are no more lines, clear the domain offset
if (Object.keys(next).length < 1) {
this.domainOffset = undefined;
}
// Update to the current set of lines
this.lines = next;
};
// Initialize the domain offset, based on these observed values
PlotUpdater.prototype.initializeDomainOffset = function (values) {
this.domainOffset =
((this.domainOffset === undefined) && (values.length > 0)) ?
(values.reduce(function (a, b) {
return (a || 0) + (b || 0);
}, 0) / values.length) :
this.domainOffset;
};
// Expand range slightly so points near edges are visible
PlotUpdater.prototype.expandRange = function () {
var padding = PADDING_RATIO * this.dimensions[1],
top;
padding = Math.max(padding, 1.0);
top = Math.ceil(this.origin[1] + this.dimensions[1] + padding / 2);
this.origin[1] = Math.floor(this.origin[1] - padding / 2);
this.dimensions[1] = top - this.origin[1];
};
// Update dimensions and origin based on extrema of plots // Update dimensions and origin based on extrema of plots
function updateBounds() { PlotUpdater.prototype.updateBounds = function () {
var bufferArray = this.bufferArray;
if (bufferArray.length > 0) { if (bufferArray.length > 0) {
domainExtrema = bufferArray.map(function (lineBuffer) { this.domainExtrema = bufferArray.map(function (lineBuffer) {
return lineBuffer.getDomainExtrema(); return lineBuffer.getDomainExtrema();
}).reduce(reduceExtrema); }).reduce(reduceExtrema);
rangeExtrema = bufferArray.map(function (lineBuffer) { this.rangeExtrema = bufferArray.map(function (lineBuffer) {
return lineBuffer.getRangeExtrema(); return lineBuffer.getRangeExtrema();
}).reduce(reduceExtrema); }).reduce(reduceExtrema);
// Calculate best-fit dimensions // Calculate best-fit dimensions
dimensions = this.dimensions = [ this.domainExtrema, this.rangeExtrema ]
[dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)]; .map(dimensionsOf);
origin = [originOf(domainExtrema), originOf(rangeExtrema)]; this.origin = [ this.domainExtrema, this.rangeExtrema ]
.map(originOf);
// Enforce some minimum visible area // Enforce some minimum visible area
expandRange(); this.expandRange();
// ...then enforce a fixed duration if needed // ...then enforce a fixed duration if needed
if (fixedDuration !== undefined) { if (this.fixedDuration !== undefined) {
origin[0] = origin[0] + dimensions[0] - fixedDuration; this.origin[0] = this.origin[0] + this.dimensions[0] -
dimensions[0] = fixedDuration; this.fixedDuration;
} this.dimensions[0] = this.fixedDuration;
}
}
// Enforce maximum duration on all plot lines; not that
// domain extrema must be up-to-date for this to behave correctly.
function enforceDuration() {
var cutoff;
function enforceDurationForBuffer(plotLineBuffer) {
var index = plotLineBuffer.findInsertionIndex(cutoff);
if (index > 0) {
// Leave one point untrimmed, such that line will
// continue off left edge of visible plot area.
plotLineBuffer.trim(index - 1);
}
}
if (fixedDuration !== undefined &&
domainExtrema !== undefined &&
(domainExtrema[1] - domainExtrema[0] > fixedDuration)) {
cutoff = domainExtrema[1] - fixedDuration;
bufferArray.forEach(enforceDurationForBuffer);
updateBounds(); // Extrema may have changed now
} }
} }
};
// Add latest data for this domain object // Add latest data for this domain object
function addPointFor(domainObject) { PlotUpdater.prototype.addPointFor = function (domainObject) {
var line = lines[domainObject.getId()]; var line = this.lines[domainObject.getId()];
if (line) { if (line) {
line.addPoint( line.addPoint(
handle.getDomainValue(domainObject, domain), this.handle.getDomainValue(domainObject, this.domain),
handle.getRangeValue(domainObject, range) this.handle.getRangeValue(domainObject, this.range)
); );
} }
} };
// Handle new telemetry data /**
function update() { * Update with latest data.
var objects = handle.getTelemetryObjects(); */
PlotUpdater.prototype.update = function update() {
var objects = this.handle.getTelemetryObjects(),
self = this;
// Initialize domain offset if necessary // Initialize domain offset if necessary
if (domainOffset === undefined) { if (this.domainOffset === undefined) {
initializeDomainOffset(objects.map(function (obj) { this.initializeDomainOffset(objects.map(function (obj) {
return handle.getDomainValue(obj, domain); return self.handle.getDomainValue(obj, self.domain);
}).filter(function (value) { }).filter(function (value) {
return typeof value === 'number'; return typeof value === 'number';
})); }));
} }
// Make sure lines are available // Make sure lines are available
prepareLines(objects); this.prepareLines(objects);
// Add new data // Add new data
objects.forEach(addPointFor); objects.forEach(function (domainObject, index) {
self.addPointFor(domainObject, index);
});
// Then, update extrema // Then, update extrema
updateBounds(); this.updateBounds();
} };
// Add historical data for this domain object
function setHistorical(domainObject, series) {
var count = series ? series.getPointCount() : 0,
line;
// Nothing to do if it's an empty series
if (count < 1) {
return;
}
// Initialize domain offset if necessary
if (domainOffset === undefined) {
initializeDomainOffset([
series.getDomainValue(0, domain),
series.getDomainValue(count - 1, domain)
]);
}
// Make sure lines are available
prepareLines(handle.getTelemetryObjects());
// Look up the line for this domain object
line = lines[domainObject.getId()];
// ...and put the data into it.
if (line) {
line.addSeries(series, domain, range);
}
// Update extrema
updateBounds();
}
// Use a default MAX_POINTS if none is provided
maxPoints = maxPoints !== undefined ? maxPoints : MAX_POINTS;
// Initially prepare state for these objects.
// Note that this may be an empty array at this time,
// so we also need to check during update cycles.
update();
return {
/** /**
* Get the dimensions which bound all data in the provided * Get the dimensions which bound all data in the provided
* data sets. This is given as a two-element array where the * data sets. This is given as a two-element array where the
* first element is domain, and second is range. * first element is domain, and second is range.
* @returns {number[]} the dimensions which bound this data set * @returns {number[]} the dimensions which bound this data set
* @memberof platform/features/plot.PlotUpdater#
*/ */
getDimensions: function () { PlotUpdater.prototype.getDimensions = function () {
return dimensions; return this.dimensions;
}, };
/** /**
* Get the origin of this data set's boundary. * Get the origin of this data set's boundary.
* This is given as a two-element array where the * This is given as a two-element array where the
* first element is domain, and second is range. * first element is domain, and second is range.
* The domain value here is not adjusted by the domain offset. * The domain value here is not adjusted by the domain offset.
* @returns {number[]} the origin of this data set's boundary * @returns {number[]} the origin of this data set's boundary
* @memberof platform/features/plot.PlotUpdater#
*/ */
getOrigin: function () { PlotUpdater.prototype.getOrigin = function () {
// Pad range if necessary return this.origin;
return origin; };
},
/** /**
* Get the domain offset; this offset will have been subtracted * Get the domain offset; this offset will have been subtracted
* from all domain values in all buffers returned by this * from all domain values in all buffers returned by this
@ -303,9 +255,10 @@ define(
* @returns {number} the domain offset * @returns {number} the domain offset
* @memberof platform/features/plot.PlotUpdater# * @memberof platform/features/plot.PlotUpdater#
*/ */
getDomainOffset: function () { PlotUpdater.prototype.getDomainOffset = function () {
return domainOffset; return this.domainOffset;
}, };
/** /**
* Get all renderable buffers for this data set. This will * Get all renderable buffers for this data set. This will
* be returned as an array which can be correlated back to * be returned as an array which can be correlated back to
@ -324,22 +277,45 @@ define(
* @returns {Float32Array[]} the buffers for these traces * @returns {Float32Array[]} the buffers for these traces
* @memberof platform/features/plot.PlotUpdater# * @memberof platform/features/plot.PlotUpdater#
*/ */
getLineBuffers: function () { PlotUpdater.prototype.getLineBuffers = function () {
return bufferArray; return this.bufferArray;
}, };
/**
* Update with latest data.
* @memberof platform/features/plot.PlotUpdater#
*/
update: update,
/** /**
* Fill in historical data. * Fill in historical data.
* @memberof platform/features/plot.PlotUpdater#
*/ */
addHistorical: setHistorical PlotUpdater.prototype.addHistorical = function (domainObject, series) {
}; var count = series ? series.getPointCount() : 0,
line;
// Nothing to do if it's an empty series
if (count < 1) {
return;
} }
// Initialize domain offset if necessary
if (this.domainOffset === undefined) {
this.initializeDomainOffset([
series.getDomainValue(0, this.domain),
series.getDomainValue(count - 1, this.domain)
]);
}
// Make sure lines are available
this.prepareLines(this.handle.getTelemetryObjects());
// Look up the line for this domain object
line = this.lines[domainObject.getId()];
// ...and put the data into it.
if (line) {
line.addSeries(series, this.domain, this.range);
}
// Update extrema
this.updateBounds();
};
return PlotUpdater; return PlotUpdater;
} }