mirror of
https://github.com/nasa/openmct.git
synced 2025-05-29 05:34:18 +00:00
Commented out console calls from gesture pinch/plot classes. Also allowed pan with single finger.
447 lines
19 KiB
JavaScript
447 lines
19 KiB
JavaScript
/*global define,window*/
|
|
|
|
define(
|
|
[
|
|
'../lib/utils'
|
|
],
|
|
function (utils) {
|
|
"use strict";
|
|
|
|
var RANGE_TICK_COUNT = 7,
|
|
DOMAIN_TICK_COUNT = 5,
|
|
ZOOM_AMT = 0.02;
|
|
|
|
function MCTPlot() {
|
|
|
|
function link($scope, $element) {
|
|
// Now that we're here, let's handle some scope management that the controller would otherwise handle.
|
|
|
|
if (typeof $scope.rectangles === "undefined") {
|
|
$scope.rectangles = [];
|
|
}
|
|
if (typeof $scope.displayableRange === "undefined") {
|
|
$scope.displayableRange = function (x) { return x; };
|
|
}
|
|
if (typeof $scope.displayableDomain === "undefined") {
|
|
$scope.displayableDomain = function (x) { return x; };
|
|
}
|
|
if (typeof $scope.axes === "undefined") {
|
|
$scope.axes = {
|
|
domain: {
|
|
label: "Time",
|
|
tickCount: DOMAIN_TICK_COUNT,
|
|
ticks: []
|
|
},
|
|
range: {
|
|
label: "Value",
|
|
tickCount: RANGE_TICK_COUNT,
|
|
ticks: []
|
|
}
|
|
};
|
|
}
|
|
|
|
var dragStart,
|
|
marqueeBox = {},
|
|
marqueeRect, // Set when exists.
|
|
chartElementBounds,
|
|
firstTouches,
|
|
firstTouch,
|
|
firstTouchDistance,
|
|
lastTouchDistance,
|
|
$canvas = $element.find('canvas');
|
|
|
|
function updateAxesForCurrentViewport() {
|
|
// Update axes definitions for current viewport.
|
|
['domain', 'range'].forEach(function (axisName) {
|
|
var axis = $scope.axes[axisName],
|
|
firstTick = $scope.viewport.topLeft[axisName],
|
|
lastTick = $scope.viewport.bottomRight[axisName],
|
|
axisSize = firstTick - lastTick,
|
|
denominator = axis.tickCount - 1,
|
|
tickNumber,
|
|
tickIncrement,
|
|
tickValue;
|
|
// Yes, ticksize is negative for domain and positive for range.
|
|
// It's because ticks are generated/displayed top to bottom and left to right.
|
|
axis.ticks = [];
|
|
for (tickNumber = 0; tickNumber < axis.tickCount; tickNumber = tickNumber + 1) {
|
|
tickIncrement = (axisSize * (tickNumber / denominator));
|
|
tickValue = firstTick - tickIncrement;
|
|
axis.ticks.push(
|
|
tickValue
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
function drawMarquee() {
|
|
// Create rectangle for Marquee if it should be set.
|
|
if (marqueeBox && marqueeBox.start && marqueeBox.end) {
|
|
if (!marqueeRect) {
|
|
marqueeRect = {};
|
|
$scope.rectangles.push(marqueeRect);
|
|
}
|
|
marqueeRect.start = marqueeBox.start;
|
|
marqueeRect.end = marqueeBox.end;
|
|
marqueeRect.color = [1, 1, 1, 0.5];
|
|
marqueeRect.layer = 'top'; // TODO: implement this.
|
|
$scope.$broadcast('rectangle-change');
|
|
} else if (marqueeRect && $scope.rectangles.indexOf(marqueeRect) !== -1) {
|
|
$scope.rectangles.splice($scope.rectangles.indexOf(marqueeRect));
|
|
marqueeRect = undefined;
|
|
$scope.$broadcast('rectangle-change');
|
|
}
|
|
}
|
|
|
|
function untrackMousePosition() {
|
|
$scope.mouseCoordinates = undefined;
|
|
}
|
|
function updateMarquee() {
|
|
// Update the marquee box in progress.
|
|
marqueeBox.end = $scope.mouseCoordinates.positionAsPlotPoint;
|
|
drawMarquee();
|
|
}
|
|
function startMarquee() {
|
|
marqueeBox.start = $scope.mouseCoordinates.positionAsPlotPoint;
|
|
}
|
|
function endMarquee() {
|
|
// marqueeBox start/end are opposite corners but we need
|
|
// topLeft and bottomRight.
|
|
var boxPoints = utils.boxPointsFromOppositeCorners(marqueeBox.start, marqueeBox.end),
|
|
newViewport = utils.oppositeCornersFromBoxPoints(boxPoints);
|
|
|
|
marqueeBox = {};
|
|
drawMarquee();
|
|
$scope.$emit('user:viewport:change:end', newViewport);
|
|
$scope.viewport = newViewport;
|
|
}
|
|
|
|
function startDrag($event) {
|
|
$scope.$emit('user:viewport:change:start');
|
|
if (!$scope.mouseCoordinates) {
|
|
return;
|
|
}
|
|
$event.preventDefault();
|
|
// Track drag location relative to position over element
|
|
// not domain, as chart viewport will change as we drag.
|
|
dragStart = $scope.mouseCoordinates.positionAsPlotPoint;
|
|
// Tell controller that we're starting to navigate.
|
|
return false;
|
|
}
|
|
|
|
function updateDrag() {
|
|
// calculate offset between points. Apply that offset to viewport.
|
|
var newPosition = $scope.mouseCoordinates.positionAsPlotPoint,
|
|
dDomain = dragStart.domain - newPosition.domain,
|
|
dRange = dragStart.range - newPosition.range;
|
|
|
|
$scope.viewport = {
|
|
topLeft: {
|
|
domain: $scope.viewport.topLeft.domain + dDomain,
|
|
range: $scope.viewport.topLeft.range + dRange
|
|
},
|
|
bottomRight: {
|
|
domain: $scope.viewport.bottomRight.domain + dDomain,
|
|
range: $scope.viewport.bottomRight.range + dRange
|
|
}
|
|
};
|
|
}
|
|
|
|
function endDrag() {
|
|
dragStart = undefined;
|
|
$scope.$emit('user:viewport:change:end', $scope.viewport);
|
|
}
|
|
|
|
function trackTouchPosition(touchPosition, bounds) {
|
|
var positionOverElement,
|
|
positionAsPlotPoint,
|
|
position;
|
|
|
|
chartElementBounds = bounds;
|
|
|
|
positionOverElement = {
|
|
x: touchPosition.clientX - bounds.left,
|
|
y: touchPosition.clientY - bounds.top
|
|
};
|
|
|
|
positionAsPlotPoint = utils.elementPositionAsPlotPosition(
|
|
positionOverElement,
|
|
bounds,
|
|
$scope.viewport
|
|
);
|
|
|
|
position = {
|
|
positionOverElement: positionOverElement,
|
|
positionAsPlotPoint: positionAsPlotPoint
|
|
};
|
|
|
|
return position;
|
|
}
|
|
|
|
function trackMousePosition($event) {
|
|
// Calculate coordinates of mouse related to canvas and as
|
|
// domain, range value and make available in scope for display.
|
|
|
|
var bounds = $event.target.getBoundingClientRect(),
|
|
positionOverElement,
|
|
positionAsPlotPoint;
|
|
|
|
chartElementBounds = bounds;
|
|
|
|
|
|
positionOverElement = {
|
|
x: $event.clientX - bounds.left,
|
|
y: $event.clientY - bounds.top
|
|
};
|
|
|
|
positionAsPlotPoint = utils.elementPositionAsPlotPosition(
|
|
positionOverElement,
|
|
bounds,
|
|
$scope.viewport
|
|
);
|
|
|
|
$scope.mouseCoordinates = {
|
|
positionOverElement: positionOverElement,
|
|
positionAsPlotPoint: positionAsPlotPoint
|
|
};
|
|
|
|
if (marqueeBox && marqueeBox.start) {
|
|
updateMarquee();
|
|
}
|
|
|
|
if (dragStart) {
|
|
updateDrag();
|
|
}
|
|
}
|
|
|
|
function watchForMarquee() {
|
|
$canvas.removeClass('plot-drag');
|
|
$canvas.addClass('plot-marquee');
|
|
$canvas.on('mousedown', startMarquee);
|
|
$canvas.on('mouseup', endMarquee);
|
|
$canvas.off('mousedown', startDrag);
|
|
$canvas.off('mouseup', endDrag);
|
|
}
|
|
|
|
function watchForDrag() {
|
|
$canvas.addClass('plot-drag');
|
|
$canvas.removeClass('plot-marquee');
|
|
$canvas.on('mousedown', startDrag);
|
|
$canvas.on('mouseup', endDrag);
|
|
$canvas.off('mousedown', startMarquee);
|
|
$canvas.off('mouseup', endMarquee);
|
|
}
|
|
|
|
function toggleInteractionMode(event) {
|
|
if (event.keyCode === 16) { // shift key.
|
|
watchForDrag();
|
|
}
|
|
}
|
|
|
|
function resetInteractionMode(event) {
|
|
if (event.keyCode === 16) {
|
|
watchForMarquee();
|
|
}
|
|
}
|
|
|
|
function stopWatching() {
|
|
$canvas.off('mousedown', startDrag);
|
|
$canvas.off('mouseup', endDrag);
|
|
$canvas.off('mousedown', startMarquee);
|
|
$canvas.off('mouseup', endMarquee);
|
|
window.removeEventListener('keydown', toggleInteractionMode);
|
|
window.removeEventListener('keyup', resetInteractionMode);
|
|
}
|
|
|
|
$canvas.on('mousemove', trackMousePosition);
|
|
$canvas.on('mouseleave', untrackMousePosition);
|
|
watchForMarquee();
|
|
|
|
window.addEventListener('keydown', toggleInteractionMode);
|
|
window.addEventListener('keyup', resetInteractionMode);
|
|
|
|
function onViewportChange() {
|
|
if ($scope.mouseCoordinates && chartElementBounds) {
|
|
$scope.mouseCoordinates.positionAsPlotPoint =
|
|
utils.elementPositionAsPlotPosition(
|
|
$scope.mouseCoordinates.positionOverElement,
|
|
chartElementBounds,
|
|
$scope.viewport
|
|
);
|
|
}
|
|
// TODO: Discuss whether marqueeBox start should be fixed to data or fixed to canvas element, especially when "isLive is true".
|
|
updateAxesForCurrentViewport();
|
|
}
|
|
|
|
function calculateDistance(coordOne, coordTwo) {
|
|
return Math.sqrt(Math.pow(coordOne.clientX - coordTwo.clientX, 2) +
|
|
Math.pow(coordOne.clientY - coordTwo.clientY, 2));
|
|
}
|
|
|
|
function setDR(midpoint) {
|
|
return {
|
|
tl: {
|
|
domain: Math.abs(midpoint.domain - ($scope.viewport.topLeft.domain)),
|
|
range: Math.abs(midpoint.range - ($scope.viewport.topLeft.range))
|
|
},
|
|
|
|
br: {
|
|
domain: Math.abs(($scope.viewport.bottomRight.domain) - midpoint.domain),
|
|
range: Math.abs(($scope.viewport.bottomRight.range) - midpoint.range)
|
|
}
|
|
};
|
|
}
|
|
|
|
function calculateViewport(midpoint, touchPosition, ratio) {
|
|
var tl,
|
|
br,
|
|
drSet = setDR(midpoint);
|
|
if (ratio < 1) {
|
|
tl = {
|
|
domain: ZOOM_AMT * drSet.tl.domain,
|
|
range: ZOOM_AMT * drSet.tl.range
|
|
};
|
|
br = {
|
|
domain: ZOOM_AMT * drSet.br.domain,
|
|
range: ZOOM_AMT * drSet.br.range
|
|
};
|
|
} else if (ratio > 1) {
|
|
tl = {
|
|
domain: - ZOOM_AMT * drSet.tl.domain,
|
|
range: - ZOOM_AMT * drSet.tl.range
|
|
};
|
|
br = {
|
|
domain: - ZOOM_AMT * drSet.br.domain,
|
|
range: - ZOOM_AMT * drSet.br.range
|
|
};
|
|
}
|
|
|
|
return {
|
|
topLeft: {
|
|
domain: (($scope.viewport.topLeft.domain) + tl.domain),
|
|
range: (($scope.viewport.topLeft.range) - tl.range)
|
|
},
|
|
bottomRight: {
|
|
domain: (($scope.viewport.bottomRight.domain) - br.domain),
|
|
range: (($scope.viewport.bottomRight.range) + br.range)
|
|
}
|
|
};
|
|
}
|
|
|
|
function updateZoom(midpoint, bounds, touches, distance) {
|
|
// calculate offset between points. Apply that offset to viewport.
|
|
var midpointPosition = trackTouchPosition(midpoint, bounds),
|
|
newMidpointPosition = midpointPosition.positionAsPlotPoint,
|
|
newTouchPosition = [trackTouchPosition(touches[0], bounds).positionAsPlotPoint,
|
|
trackTouchPosition(touches[1], bounds).positionAsPlotPoint],
|
|
distanceRatio = lastTouchDistance / distance || firstTouchDistance / distance,
|
|
newViewport = calculateViewport(newMidpointPosition, newTouchPosition, distanceRatio);
|
|
|
|
$scope.viewport = newViewport;
|
|
}
|
|
|
|
function startZoom(midpoint, bounds, touches, distance) {
|
|
firstTouches = [trackTouchPosition(touches[0], bounds).positionAsPlotPoint,
|
|
trackTouchPosition(touches[1], bounds).positionAsPlotPoint];
|
|
firstTouchDistance = distance;
|
|
firstTouch = trackTouchPosition(midpoint, bounds).positionAsPlotPoint;
|
|
}
|
|
|
|
function updatePan(touch, bounds) {
|
|
// calculate offset between points. Apply that offset to viewport.
|
|
var panPosition = trackTouchPosition(touch, bounds),
|
|
newPanPosition = panPosition.positionAsPlotPoint,
|
|
dDomain = firstTouch.domain - newPanPosition.domain,
|
|
dRange = firstTouch.range - newPanPosition.range;
|
|
|
|
$scope.viewport = {
|
|
topLeft: {
|
|
domain: (($scope.viewport.topLeft.domain) + dDomain),
|
|
range: (($scope.viewport.topLeft.range) + dRange)
|
|
},
|
|
bottomRight: {
|
|
domain: (($scope.viewport.bottomRight.domain) + dDomain),
|
|
range: (($scope.viewport.bottomRight.range) + dRange)
|
|
}
|
|
};
|
|
}
|
|
|
|
function startPan(touch, bounds) {
|
|
$scope.$emit('user:viewport:change:start');
|
|
firstTouch = trackTouchPosition(touch, bounds).positionAsPlotPoint;
|
|
}
|
|
|
|
function endTouch() {
|
|
$scope.$emit('user:viewport:change:end', $scope.viewport);
|
|
}
|
|
|
|
function onPinchStart(event, touch) {
|
|
$scope.$emit('user:viewport:change:start');
|
|
startZoom(touch.midpoint, touch.bounds, touch.touches, touch.distance);
|
|
|
|
}
|
|
|
|
function comparePinchDrag(distance, firstDistance, lastDistance) {
|
|
var amt = 2;
|
|
return (((firstDistance += amt) >= distance) && ((firstDistance -= amt) <= distance))
|
|
|| (((lastDistance += amt) >= distance) && ((lastDistance -= amt) <= distance));
|
|
}
|
|
|
|
function onPinchChange(event, touch) {
|
|
//console.log(Math.round(touch.distance));
|
|
|
|
if(comparePinchDrag(Math.round(touch.distance), Math.round(firstTouchDistance),
|
|
Math.round(lastTouchDistance))) {
|
|
//console.log("# PINCH PAN");
|
|
updatePan(touch.midpoint, touch.bounds);
|
|
} else {
|
|
//console.log("# PINCH ZOOM");
|
|
updateZoom(touch.midpoint, touch.bounds, touch.touches, touch.distance);
|
|
}
|
|
lastTouchDistance = touch.distance;
|
|
}
|
|
|
|
function onPanStart(event, touch) {
|
|
startPan(touch.touch, touch.bounds);
|
|
}
|
|
|
|
function onPanChange(event, touch) {
|
|
updatePan(touch.touch, touch.bounds);
|
|
}
|
|
|
|
function onTouchEnd(event) {
|
|
endTouch();
|
|
}
|
|
|
|
$scope.$watchCollection('viewport', onViewportChange);
|
|
|
|
$scope.$on('mct:pan:start', onPanStart);
|
|
$scope.$on('mct:pan:change', onPanChange);
|
|
|
|
$scope.$on('mct:pinch:start', onPinchStart);
|
|
$scope.$on('mct:pinch:change', onPinchChange);
|
|
|
|
$scope.$on('mct:ptouch:end', onTouchEnd);
|
|
|
|
$scope.$on('$destroy', stopWatching);
|
|
}
|
|
|
|
return {
|
|
restrict: "E",
|
|
templateUrl: 'platform/features/plot-reborn/res/templates/mct-plot.html',
|
|
link: link,
|
|
scope: {
|
|
viewport: "=",
|
|
series: "=",
|
|
rectangles: "=?",
|
|
axes: "=?",
|
|
displayableRange: "=?",
|
|
displayableDomain: "=?"
|
|
}
|
|
};
|
|
}
|
|
|
|
return MCTPlot;
|
|
}
|
|
);
|