/*global define,moment,Promise*/ /** * Module defining PlotController. Created by vwoeltje on 11/12/14. */ define( [ "./PlotPreparer", "./PlotPalette", "./PlotPanZoomStack", "./PlotPosition", "./PlotTickGenerator", "./PlotFormatter" ], function (PlotPreparer, PlotPalette, PlotPanZoomStack, PlotPosition, PlotTickGenerator, PlotFormatter) { "use strict"; var AXIS_DEFAULTS = [ { "name": "Time" }, { "name": "Value" } ], DOMAIN_TICKS = 5, RANGE_TICKS = 7; /** * * @constructor */ function PlotController($scope) { var mousePosition, marqueeStart, panZoomStack = new PlotPanZoomStack([], []), formatter = new PlotFormatter(), domainOffset; // Utility, for map/forEach loops. Index 0 is domain, // index 1 is range. function formatValue(v, i) { return (i ? formatter.formatRangeValue : formatter.formatDomainValue)(v); } function mousePositionToDomainRange(mousePosition, domainOffset) { return new PlotPosition( mousePosition.x, mousePosition.y, mousePosition.width, mousePosition.height, panZoomStack, domainOffset ).getPosition(); } function updateMarqueeBox() { $scope.draw.boxes = marqueeStart ? [{ start: mousePositionToDomainRange(marqueeStart), end: mousePositionToDomainRange(mousePosition), color: [1, 1, 1, 0.5 ] }] : undefined; } function updateDrawingBounds() { var panZoom = panZoomStack.getPanZoom(); $scope.draw.dimensions = panZoom.dimensions; $scope.draw.origin = panZoom.origin; } function plotTelemetry() { var telemetry, prepared, tickGenerator, data; telemetry = $scope.telemetry; if (!telemetry) { return; } data = telemetry.getResponse(); prepared = new PlotPreparer( data, ($scope.axes[0].active || {}).key, ($scope.axes[1].active || {}).key ); tickGenerator = new PlotTickGenerator(prepared, formatter); $scope.axes[0].ticks = tickGenerator.generateDomainTicks(DOMAIN_TICKS); $scope.axes[1].ticks = tickGenerator.generateRangeTicks(RANGE_TICKS); panZoomStack.setBasePanZoom( prepared.getOrigin(), prepared.getDimensions() ); domainOffset = prepared.getDomainOffset(); $scope.draw.lines = prepared.getBuffers().map(function (buf, i) { return { buffer: buf, color: PlotPalette.getFloatColor(i), points: buf.length / 2 }; }); updateDrawingBounds(); updateMarqueeBox(); } function setupAxes(metadatas) { var domainKeys = {}, rangeKeys = {}, domains = [], ranges = []; function buildOptionsForMetadata(m) { (m.domains || []).forEach(function (domain) { if (!domainKeys[domain.key]) { domainKeys[domain.key] = true; domains.push(domain); } }); (m.ranges || []).forEach(function (range) { if (!rangeKeys[range.key]) { rangeKeys[range.key] = true; ranges.push(range); } }); } (metadatas || []). forEach(buildOptionsForMetadata); [domains, ranges].forEach(function (options, i) { var active = $scope.axes[i].active; $scope.axes[i].options = options; if (!active || !active.key) { $scope.axes[i].active = options[0] || AXIS_DEFAULTS[i]; } }); } function toMousePosition($event) { var target = $event.target, bounds = target.getBoundingClientRect(); return { x: $event.clientX - bounds.left, y: $event.clientY - bounds.top, width: bounds.width, height: bounds.height }; } function marqueeZoom(start, end) { var a = mousePositionToDomainRange(start), b = mousePositionToDomainRange(end), origin = [ Math.min(a[0], b[0]), Math.min(a[1], b[1]) ], dimensions = [ Math.max(a[0], b[0]) - origin[0], Math.max(a[1], b[1]) - origin[1] ]; panZoomStack.pushPanZoom(origin, dimensions); } $scope.axes = [ {}, {} ]; $scope.$watch("telemetry.getMetadata()", setupAxes); $scope.$on("telemetryUpdate", plotTelemetry); $scope.draw = {}; return { getColor: function (index) { return PlotPalette.getStringColor(index); }, getHoverCoordinates: function () { return mousePosition ? mousePositionToDomainRange( mousePosition, domainOffset ).map(formatValue) : []; }, hover: function ($event) { mousePosition = toMousePosition($event); if (marqueeStart) { updateMarqueeBox(); } }, startMarquee: function ($event) { mousePosition = marqueeStart = toMousePosition($event); updateMarqueeBox(); }, endMarquee: function ($event) { mousePosition = toMousePosition($event); if (marqueeStart) { marqueeZoom(marqueeStart, mousePosition); marqueeStart = undefined; updateMarqueeBox(); updateDrawingBounds(); } }, isZoomed: function () { return panZoomStack.getDepth() > 1; }, stepBackPanZoom: function () { panZoomStack.popPanZoom(); updateDrawingBounds(); }, unzoom: function () { panZoomStack.clearPanZoom(); updateDrawingBounds(); } }; } return PlotController; } );