MCTChart preserves precision of plot values

This commit is contained in:
Pete Richards 2015-08-12 13:59:32 -07:00
parent b40494ac95
commit bb8c8a75ab
2 changed files with 75 additions and 24 deletions

View File

@ -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);

View File

@ -10,6 +10,26 @@ define(
var TEMPLATE = "<canvas style='position: absolute; background: none; width: 100%; height: 100%;'></canvas>";
/**
* 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();