diff --git a/platform/features/plot-reborn/src/controllers/PlotController.js b/platform/features/plot-reborn/src/controllers/PlotController.js index 4889ea004a..afdd90e9a4 100644 --- a/platform/features/plot-reborn/src/controllers/PlotController.js +++ b/platform/features/plot-reborn/src/controllers/PlotController.js @@ -11,10 +11,10 @@ define( function PlotController($scope) { var plotHistory = []; var isLive = true; - var maxDomain = 0; - var domainOffset = +new Date(); + var maxDomain = +new Date(); var subscriptions = []; var setToDefaultViewport = function() { + // TODO: We shouldn't set the viewport until we have received data or something has given us a reasonable viewport. $scope.viewport = { topLeft: { domain: maxDomain - DOMAIN_INTERVAL, @@ -35,7 +35,7 @@ define( }; $scope.displayableDomain = function(domainValue) { // TODO: Call format function provided by domain object. - return new Date(domainValue + domainOffset).toUTCString(); + return new Date(domainValue).toUTCString(); }; $scope.series = []; @@ -43,10 +43,7 @@ define( $scope.rectangles = []; var updateSeriesFromTelemetry = function(series, seriesIndex, telemetry) { - if (typeof domainOffset === 'undefined') { - domainOffset = telemetry.getDomainValue(telemetry.getPointCount() - 1); - } - var domainValue = telemetry.getDomainValue(telemetry.getPointCount() - 1) - domainOffset; + var domainValue = telemetry.getDomainValue(telemetry.getPointCount() - 1); var rangeValue = telemetry.getRangeValue(telemetry.getPointCount() - 1); // Track the biggest domain we've seen for sticky-ness. maxDomain = Math.max(maxDomain, domainValue); diff --git a/platform/features/plot-reborn/src/directives/MCTChart.js b/platform/features/plot-reborn/src/directives/MCTChart.js index 7b88a43243..4a9ba6b574 100644 --- a/platform/features/plot-reborn/src/directives/MCTChart.js +++ b/platform/features/plot-reborn/src/directives/MCTChart.js @@ -10,6 +10,26 @@ define( var TEMPLATE = ""; + /** + * Offsetter adjusts domain and range values by a fixed amount, + * generally increasing the precision of the 32 bit float representation + * required for plotting. + * + * @constructor + */ + function Offsetter(domainOffset, rangeOffset) { + this.domainOffset = domainOffset; + this.rangeOffset = rangeOffset; + } + + Offsetter.prototype.domain = function(dataDomain) { + return dataDomain - this.domainOffset; + }; + + Offsetter.prototype.range = function(dataRange) { + return dataRange - this.rangeOffset; + }; + /** * MCTChart draws charts utilizing a drawAPI. * @@ -22,7 +42,8 @@ define( isDestroyed = false, activeInterval, drawAPI, - lines = []; + lines = [], + offset; drawAPI = DrawLoader.getDrawAPI(canvas); @@ -34,15 +55,35 @@ define( if (isDestroyed) { return; } + requestAnimationFrame(redraw); canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; drawAPI.clear(); + createOffset(); + if (!offset) { + return; + } updateViewport(); drawSeries(); drawRectangles(); } + function createOffset() { + if (offset) { + return; + } + if (!$scope.viewport || + !$scope.viewport.topLeft || + !$scope.viewport.bottomRight) { + return; + } + offset = new Offsetter( + $scope.viewport.topLeft.domain, + $scope.viewport.topLeft.range + ); + } + function drawIfResized() { if (canvas.width !== canvas.offsetWidth || canvas.height !== canvas.offsetHeight) { @@ -59,6 +100,9 @@ define( function drawSeries() { // TODO: Don't regenerate lines on each frame. + if (!$scope.series || !$scope.series.length) { + return; + } lines = $scope.series.map(lineFromSeries); lines.forEach(function(line) { drawAPI.drawLine( @@ -73,8 +117,14 @@ define( if ($scope.rectangles) { $scope.rectangles.forEach(function(rect) { drawAPI.drawSquare( - [rect.start.domain, rect.start.range], - [rect.end.domain, rect.end.range], + [ + offset.domain(rect.start.domain), + offset.range(rect.start.range) + ], + [ + offset.domain(rect.end.domain), + offset.range(rect.end.range) + ], rect.color ); }); @@ -83,13 +133,23 @@ define( function updateViewport() { var dimensions = [ - Math.abs($scope.viewport.topLeft.domain - $scope.viewport.bottomRight.domain), - Math.abs($scope.viewport.topLeft.range - $scope.viewport.bottomRight.range) + Math.abs( + offset.domain($scope.viewport.topLeft.domain) - + offset.domain($scope.viewport.bottomRight.domain) + ), + Math.abs( + offset.range($scope.viewport.topLeft.range) - + offset.range($scope.viewport.bottomRight.range) + ) ]; var origin = [ - $scope.viewport.topLeft.domain, - $scope.viewport.bottomRight.range + offset.domain( + $scope.viewport.topLeft.domain + ), + offset.range( + $scope.viewport.bottomRight.range + ) ]; drawAPI.setDimensions( @@ -113,8 +173,8 @@ define( // appears minimal. var lineBuffer = new Float32Array(20000); for (var i = 0; i < series.data.length; i++) { - lineBuffer[2*i] = series.data[i].domain; - lineBuffer[2*i+1] = series.data[i].range; + lineBuffer[2*i] = offset.domain(series.data[i].domain); + lineBuffer[2*i+1] = offset.range(series.data[i].range); } return { color: series.color, @@ -123,15 +183,11 @@ define( }; } - function initializeLines() { - lines = $scope.series.map(lineFromSeries); - } - function onSeriesDataAdd(event, seriesIndex, points) { var line = lines[seriesIndex]; points.forEach(function (point) { - line.buffer[2*line.pointCount] = point.domain; - line.buffer[2*line.pointCount+1] = point.range; + line.buffer[2*line.pointCount] = offset.domain(point.domain); + line.buffer[2*line.pointCount+1] = offset.range(point.range); line.pointCount += 1; }); } @@ -139,8 +195,6 @@ define( // Check for resize, on a timer activeInterval = $interval(drawIfResized, 1000); - // Initialize series - $scope.$watch('series', initializeLines); $scope.$on('series:data:add', onSeriesDataAdd); redraw();