Merge remote-tracking branch 'origin/open1273' into open-1340

This commit is contained in:
larkin
2015-06-26 15:03:19 -07:00
10 changed files with 176 additions and 44 deletions

View File

@ -57,13 +57,17 @@ define(
// mouse may leave this element during the drag. // mouse may leave this element during the drag.
var body = $document.find('body'), var body = $document.find('body'),
initialPosition, initialPosition,
$event,
delta; delta;
// Utility function to cause evaluation of mctDrag, // Utility function to cause evaluation of mctDrag,
// mctDragUp, etc // mctDragUp, etc
function fireListener(name) { function fireListener(name) {
// Evaluate the expression, with current delta // Evaluate the expression, with current delta
scope.$eval(attrs[name], { delta: delta }); scope.$eval(attrs[name], {
delta: delta,
$event: $event
});
// Trigger prompt digestion // Trigger prompt digestion
scope.$apply(); scope.$apply();
@ -82,6 +86,9 @@ define(
delta = currentPosition.map(function (v, i) { delta = currentPosition.map(function (v, i) {
return v - initialPosition[i]; return v - initialPosition[i];
}); });
// Also track the plain event for firing listeners
$event = event;
} }
// Called during a drag, on mousemove // Called during a drag, on mousemove
@ -106,7 +113,7 @@ define(
fireListener("mctDragUp"); fireListener("mctDragUp");
// Clear out start-of-drag position // Clear out start-of-drag position, target
initialPosition = undefined; initialPosition = undefined;
// Don't show selection highlights, etc // Don't show selection highlights, etc
@ -131,6 +138,7 @@ define(
// Don't show selection highlights, etc // Don't show selection highlights, etc
event.preventDefault(); event.preventDefault();
return false; return false;
} }

View File

@ -81,10 +81,11 @@ define(
}); });
it("invokes mctDragDown when dragging begins", function () { it("invokes mctDragDown when dragging begins", function () {
mockElement.on.mostRecentCall.args[1](testEvent(42, 60)); var event = testEvent(42, 60);
mockElement.on.mostRecentCall.args[1](event);
expect(mockScope.$eval).toHaveBeenCalledWith( expect(mockScope.$eval).toHaveBeenCalledWith(
testAttrs.mctDragDown, testAttrs.mctDragDown,
{ delta: [0, 0] } { delta: [0, 0], $event: event }
); );
}); });
@ -101,23 +102,27 @@ define(
}); });
it("invokes mctDrag expression during drag", function () { it("invokes mctDrag expression during drag", function () {
var event;
mockElement.on.mostRecentCall.args[1](testEvent(42, 60)); mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
// Find and invoke the mousemove listener // Find and invoke the mousemove listener
mockBody.on.calls.forEach(function (call) { mockBody.on.calls.forEach(function (call) {
if (call.args[0] === 'mousemove') { if (call.args[0] === 'mousemove') {
call.args[1](testEvent(52, 200)); call.args[1](event = testEvent(52, 200));
} }
}); });
// Should have passed that delta to mct-drag expression // Should have passed that delta to mct-drag expression
expect(mockScope.$eval).toHaveBeenCalledWith( expect(mockScope.$eval).toHaveBeenCalledWith(
testAttrs.mctDrag, testAttrs.mctDrag,
{ delta: [10, 140] } { delta: [10, 140], $event: event }
); );
}); });
it("invokes mctDragUp expression after drag", function () { it("invokes mctDragUp expression after drag", function () {
var event;
mockElement.on.mostRecentCall.args[1](testEvent(42, 60)); mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
// Find and invoke the mousemove listener // Find and invoke the mousemove listener
@ -129,7 +134,7 @@ define(
// Find and invoke the mousemove listener // Find and invoke the mousemove listener
mockBody.on.calls.forEach(function (call) { mockBody.on.calls.forEach(function (call) {
if (call.args[0] === 'mouseup') { if (call.args[0] === 'mouseup') {
call.args[1](testEvent(40, 71)); call.args[1](event = testEvent(40, 71));
} }
}); });
@ -138,7 +143,7 @@ define(
// initial position // initial position
expect(mockScope.$eval).toHaveBeenCalledWith( expect(mockScope.$eval).toHaveBeenCalledWith(
testAttrs.mctDragUp, testAttrs.mctDragUp,
{ delta: [-2, 11] } { delta: [-2, 11], $event: event }
); );
// Should also have unregistered listeners // Should also have unregistered listeners

View File

@ -23,7 +23,21 @@
{ {
"key": "PlotController", "key": "PlotController",
"implementation": "PlotController.js", "implementation": "PlotController.js",
"depends": [ "$scope", "telemetryFormatter", "telemetryHandler", "throttle" ] "depends": [
"$scope",
"telemetryFormatter",
"telemetryHandler",
"throttle",
"PLOT_FIXED_DURATION"
]
}
],
"constants": [
{
"key": "PLOT_FIXED_DURATION",
"value": 900000,
"priority": "fallback",
"comment": "Fifteen minutes."
} }
], ],
"policies": [ "policies": [

View File

@ -82,8 +82,9 @@
<mct-chart draw="subplot.getDrawingObject()" <mct-chart draw="subplot.getDrawingObject()"
ng-mousemove="subplot.hover($event)" ng-mousemove="subplot.hover($event)"
ng-mousedown="subplot.startMarquee($event)" mct-drag="subplot.continueDrag($event)"
ng-mouseup="subplot.endMarquee($event); plot.update()"> mct-drag-down="subplot.startDrag($event)"
mct-drag-up="subplot.endDrag($event); plot.update()">
</mct-chart> </mct-chart>
<!-- TODO: Move into correct position; make part of group; infer from set of actions --> <!-- TODO: Move into correct position; make part of group; infer from set of actions -->

View File

@ -51,7 +51,13 @@ define(
* *
* @constructor * @constructor
*/ */
function PlotController($scope, telemetryFormatter, telemetryHandler, throttle) { function PlotController(
$scope,
telemetryFormatter,
telemetryHandler,
throttle,
PLOT_FIXED_DURATION
) {
var subPlotFactory = new SubPlotFactory(telemetryFormatter), var subPlotFactory = new SubPlotFactory(telemetryFormatter),
modeOptions = new PlotModeOptions([], subPlotFactory), modeOptions = new PlotModeOptions([], subPlotFactory),
subplots = [], subplots = [],
@ -99,7 +105,8 @@ define(
updater = new PlotUpdater( updater = new PlotUpdater(
handle, handle,
($scope.axes[0].active || {}).key, ($scope.axes[0].active || {}).key,
($scope.axes[1].active || {}).key ($scope.axes[1].active || {}).key,
PLOT_FIXED_DURATION
); );
} }

View File

@ -56,6 +56,9 @@ define(
domainOffset, domainOffset,
mousePosition, mousePosition,
marqueeStart, marqueeStart,
panStart,
panStartBounds,
subPlotBounds,
hoverCoordinates, hoverCoordinates,
isHovering = false; isHovering = false;
@ -88,8 +91,7 @@ define(
// pixel coordinates in the canvas area) from a mouse // pixel coordinates in the canvas area) from a mouse
// event object. // event object.
function toMousePosition($event) { function toMousePosition($event) {
var target = $event.target, var bounds = subPlotBounds;
bounds = target.getBoundingClientRect();
return { return {
x: $event.clientX - bounds.left, x: $event.clientX - bounds.left,
@ -155,6 +157,25 @@ define(
tickGenerator.generateRangeTicks(RANGE_TICKS); tickGenerator.generateRangeTicks(RANGE_TICKS);
} }
function updatePan() {
var start, current, delta, nextOrigin;
// Clear the previous panning pan-zoom state
panZoomStack.popPanZoom();
// Calculate what the new resulting pan-zoom should be
start = mousePositionToDomainRange(panStart);
current = mousePositionToDomainRange(mousePosition);
delta = [ current[0] - start[0], current[1] - start[1] ];
nextOrigin = [
panStartBounds.origin[0] - delta[0],
panStartBounds.origin[1] - delta[1]
];
// ...and push a new one at the current mouse position
panZoomStack.pushPanZoom(nextOrigin, panStartBounds.dimensions);
}
// Perform a marquee zoom. // Perform a marquee zoom.
function marqueeZoom(start, end) { function marqueeZoom(start, end) {
@ -241,31 +262,77 @@ define(
*/ */
hover: function ($event) { hover: function ($event) {
isHovering = true; isHovering = true;
subPlotBounds = $event.target.getBoundingClientRect();
mousePosition = toMousePosition($event); mousePosition = toMousePosition($event);
updateHoverCoordinates(); updateHoverCoordinates();
if (marqueeStart) { if (marqueeStart) {
updateMarqueeBox(); updateMarqueeBox();
} }
if (panStart) {
updatePan();
updateDrawingBounds();
updateTicks();
}
},
/**
* Continue a previously-start pan or zoom gesture.
* @param $event the mouse event
*/
continueDrag: function ($event) {
mousePosition = toMousePosition($event);
if (marqueeStart) {
updateMarqueeBox();
}
if (panStart) {
updatePan();
updateDrawingBounds();
updateTicks();
}
}, },
/** /**
* Initiate a marquee zoom action. * Initiate a marquee zoom action.
* @param $event the mouse event * @param $event the mouse event
*/ */
startMarquee: function ($event) { startDrag: function ($event) {
mousePosition = marqueeStart = toMousePosition($event); subPlotBounds = $event.target.getBoundingClientRect();
mousePosition = toMousePosition($event);
// Treat any modifier key as a pan
if ($event.altKey || $event.shiftKey || $event.ctrlKey) {
// Start panning
panStart = mousePosition;
panStartBounds = panZoomStack.getPanZoom();
// We're starting a pan, so add this back as a
// state on the stack; it will get replaced
// during the pan.
panZoomStack.pushPanZoom(
panStartBounds.origin,
panStartBounds.dimensions
);
$event.preventDefault();
} else {
// Start marquee zooming
marqueeStart = mousePosition;
updateMarqueeBox(); updateMarqueeBox();
}
}, },
/** /**
* Complete a marquee zoom action. * Complete a marquee zoom action.
* @param $event the mouse event * @param $event the mouse event
*/ */
endMarquee: function ($event) { endDrag: function ($event) {
mousePosition = toMousePosition($event); mousePosition = toMousePosition($event);
subPlotBounds = undefined;
if (marqueeStart) { if (marqueeStart) {
marqueeZoom(marqueeStart, mousePosition); marqueeZoom(marqueeStart, mousePosition);
marqueeStart = undefined; marqueeStart = undefined;
updateMarqueeBox(); updateMarqueeBox();
updateDrawingBounds(); updateDrawingBounds();
updateTicks();
}
if (panStart) {
// End panning
panStart = undefined;
panStartBounds = undefined;
} }
}, },
/** /**

View File

@ -43,9 +43,9 @@ define(
var mid = Math.floor((min + max) / 2), var mid = Math.floor((min + max) / 2),
found = buffer[mid * 2]; found = buffer[mid * 2];
// Collisions are not wanted // On collisions, insert at same index
if (found === value) { if (found === value) {
return -1; return mid;
} }
// Otherwise, if we're down to a single index, // Otherwise, if we're down to a single index,

View File

@ -42,8 +42,10 @@ 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} maxPoints maximum number of points to display
*/ */
function PlotUpdater(handle, domain, range, maxPoints) { function PlotUpdater(handle, domain, range, fixedDuration, maxPoints) {
var ids = [], var ids = [],
lines = {}, lines = {},
dimensions = [0, 0], dimensions = [0, 0],
@ -107,6 +109,7 @@ define(
lines = next; lines = next;
} }
// Initialize the domain offset, based on these observed values // Initialize the domain offset, based on these observed values
function initializeDomainOffset(values) { function initializeDomainOffset(values) {
domainOffset = domainOffset =
@ -133,7 +136,7 @@ define(
} }
// Update dimensions and origin based on extrema of plots // Update dimensions and origin based on extrema of plots
function updateExtrema() { function updateBounds() {
if (bufferArray.length > 0) { if (bufferArray.length > 0) {
domainExtrema = bufferArray.map(function (lineBuffer) { domainExtrema = bufferArray.map(function (lineBuffer) {
return lineBuffer.getDomainExtrema(); return lineBuffer.getDomainExtrema();
@ -143,10 +146,40 @@ define(
return lineBuffer.getRangeExtrema(); return lineBuffer.getRangeExtrema();
}).reduce(reduceExtrema); }).reduce(reduceExtrema);
// Calculate best-fit dimensions
dimensions = (rangeExtrema[0] === rangeExtrema[1]) ? dimensions = (rangeExtrema[0] === rangeExtrema[1]) ?
[dimensionsOf(domainExtrema), 2.0 ] : [dimensionsOf(domainExtrema), 2.0 ] :
[dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)]; [dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)];
origin = [originOf(domainExtrema), originOf(rangeExtrema)]; origin = [originOf(domainExtrema), originOf(rangeExtrema)];
// ...then enforce a fixed duration if needed
if (fixedDuration !== undefined) {
origin[0] = origin[0] + dimensions[0] - fixedDuration;
dimensions[0] = 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
} }
} }
@ -180,8 +213,8 @@ define(
// Add new data // Add new data
objects.forEach(addPointFor); objects.forEach(addPointFor);
// Finally, update extrema // Then, update extrema
updateExtrema(); updateBounds();
} }
// Add historical data for this domain object // Add historical data for this domain object
@ -213,12 +246,12 @@ define(
line.addSeries(series, domain, range); line.addSeries(series, domain, range);
} }
// Finally, update extrema // Update extrema
updateExtrema(); updateBounds();
} }
// Use a default MAX_POINTS if none is provided // Use a default MAX_POINTS if none is provided
maxPoints = maxPoints || MAX_POINTS; maxPoints = maxPoints !== undefined ? maxPoints : MAX_POINTS;
// Initially prepare state for these objects. // Initially prepare state for these objects.
// Note that this may be an empty array at this time, // Note that this may be an empty array at this time,

View File

@ -127,7 +127,7 @@ define(
// Simulate a marquee zoom. Note that the mockElement // Simulate a marquee zoom. Note that the mockElement
// is 100 by 100 and starts at 10,20 // is 100 by 100 and starts at 10,20
subplot.startMarquee({ subplot.startDrag({
target: mockElement, target: mockElement,
clientX: 60, clientX: 60,
clientY: 45 clientY: 45
@ -137,7 +137,7 @@ define(
clientX: 75, clientX: 75,
clientY: 85 clientY: 85
}); });
subplot.endMarquee({ subplot.endDrag({
target: mockElement, target: mockElement,
clientX: 80, clientX: 80,
clientY: 95 clientY: 95
@ -162,7 +162,7 @@ define(
// Simulate a marquee zoom. Note that the mockElement // Simulate a marquee zoom. Note that the mockElement
// is 100 by 100 and starts at 10,20 // is 100 by 100 and starts at 10,20
subplot.startMarquee({ subplot.startDrag({
target: mockElement, target: mockElement,
clientX: 60, clientX: 60,
clientY: 45 clientY: 45
@ -172,7 +172,7 @@ define(
clientX: 75, clientX: 75,
clientY: 85 clientY: 85
}); });
subplot.endMarquee({ subplot.endDrag({
target: mockElement, target: mockElement,
clientX: 60, clientX: 60,
clientY: 45 clientY: 45

View File

@ -83,9 +83,6 @@ define(
expect(buffer.findInsertionIndex(10)).toEqual(4); expect(buffer.findInsertionIndex(10)).toEqual(4);
expect(buffer.findInsertionIndex(14.5)).toEqual(5); expect(buffer.findInsertionIndex(14.5)).toEqual(5);
expect(buffer.findInsertionIndex(20)).toEqual(6); expect(buffer.findInsertionIndex(20)).toEqual(6);
// 9 is already in there, disallow insertion
expect(buffer.findInsertionIndex(9)).toEqual(-1);
}); });
it("allows insertion in the middle", function () { it("allows insertion in the middle", function () {