Skip to content
125 changes: 100 additions & 25 deletions src/dygraph-interaction-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,15 @@ DygraphInteraction.endZoom = function(event, g, context) {
context.dragStartY = null;
};

// isOutOfExtremes: checks that number p is out of extremes ex.
// ex is a tuple [a, b], with a <= b
function isOutOfExtremes(p, ex) {

return p < ex[0] || p > ex[1]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please do add explicit semicola at the end of instructions (not just here but everywhere); semicolon insertion is considered harmful


// note on ex[0] < ex[1]: sometimes extremes are invalid,
// ex[0] must be less than or equal to ex[1]
}
/**
* @private
*/
Expand All @@ -418,17 +427,40 @@ DygraphInteraction.startTouch = function(event, g, context) {
var touches = [];
for (var i = 0; i < event.touches.length; i++) {
var t = event.touches[i];
var rect = t.target.getBoundingClientRect()
// we dispense with 'dragGetX_' because all touchBrowsers support pageX
touches.push({
pageX: t.pageX,
pageY: t.pageY,
dataX: g.toDataXCoord(t.pageX),
dataY: g.toDataYCoord(t.pageY)
dataX: g.toDataXCoord(t.clientX-rect.left),
dataY: g.toDataYCoord(t.clientY-rect.top)
// identifier: t.identifier
});
}
context.initialTouches = touches;

var pinchCenter
context.pinchOutOfExtremes = false
if (touches.length >= 2) {

// only screen coordinates can be averaged (data coords could be log scale).
pinchCenter = {
pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
pageY: 0.5 * (touches[0].pageY + touches[1].pageY),

// TODO(danvk): remove
dataX: 0.5 * (touches[0].dataX + touches[1].dataX),
dataY: 0.5 * (touches[0].dataY + touches[1].dataY)
};

var xExtremes = g.xAxisExtremes()
var yExtremes = g.yAxisExtremes()
if (xExtremes[0] >= xExtremes[1] || isOutOfExtremes(pinchCenter.dataX, xExtremes))
context.pinchOutOfExtremes = true
if (yExtremes.find(yEx => yEx[0] >= yEx[1] || isOutOfExtremes(pinchCenter.dataY, yEx)))
context.pinchOutOfExtremes = true
}

if (touches.length == 1) {
// This is just a swipe.
context.initialPinchCenter = touches[0];
Expand All @@ -437,15 +469,7 @@ DygraphInteraction.startTouch = function(event, g, context) {
// It's become a pinch!
// In case there are 3+ touches, we ignore all but the "first" two.

// only screen coordinates can be averaged (data coords could be log scale).
context.initialPinchCenter = {
pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
pageY: 0.5 * (touches[0].pageY + touches[1].pageY),

// TODO(danvk): remove
dataX: 0.5 * (touches[0].dataX + touches[1].dataX),
dataY: 0.5 * (touches[0].dataY + touches[1].dataY)
};
context.initialPinchCenter = pinchCenter

// Make pinches in a 45-degree swath around either axis 1-dimensional zooms.
var initialAngle = 180 / Math.PI * Math.atan2(
Expand All @@ -457,8 +481,11 @@ DygraphInteraction.startTouch = function(event, g, context) {
if (initialAngle > 90) initialAngle = 90 - initialAngle;

context.touchDirections = {
x: (initialAngle < (90 - 45/2)),
y: (initialAngle > 45/2)
//TODO: it does not seem to work well
//x: (initialAngle < (90 - 45/2)),
//y: (initialAngle > 45/2)
x: true,
y: true
};
}

Expand Down Expand Up @@ -509,31 +536,62 @@ DygraphInteraction.moveTouch = function(event, g, context) {
var dataHeight = context.initialRange.y[0] - context.initialRange.y[1];
swipe.dataX = (swipe.pageX / g.plotter_.area.w) * dataWidth;
swipe.dataY = (swipe.pageY / g.plotter_.area.h) * dataHeight;
var xScale, yScale;

var xExtremes = g.xAxisExtremes()
var yExtremes = g.yAxisExtremes()

var xScale = 1.0, yScale = 1.0;

// The residual bits are usually split into scale & rotate bits, but we split
// them into x-scale and y-scale bits.
if (touches.length == 1) {
xScale = 1.0;
yScale = 1.0;
} else if (touches.length >= 2) {
if (touches.length >= 2 && !context.pinchOutOfExtremes) {
var initHalfWidth = (initialTouches[1].pageX - c_init.pageX);
xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth;

var initHalfHeight = (initialTouches[1].pageY - c_init.pageY);
yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight;
var minAllowed = 5

if (Math.abs(initHalfWidth) > minAllowed) {
// sensitiveness dampening: smaller pinches count much less
var damp = 1 / (Math.abs(initHalfWidth)-minAllowed)

var nowHalfWidth = touches[1].pageX - c_now.pageX
xScale = (nowHalfWidth + damp) / (initHalfWidth + damp);
}

if (Math.abs(initHalfHeight) > minAllowed) {
var damp = 1 / (Math.abs(initHalfHeight) - minAllowed)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT var has function scope, so this is an error. I’d say declare var damp above with the initHalf{Width,Height} and reuse it in both branches.


var nowHalfHeight = touches[1].pageY - c_now.pageY
yScale = (nowHalfHeight + damp) / (initHalfHeight + damp);
}
}

// Clip scaling to [1/8, 8] to prevent too much blowup.
xScale = Math.min(8, Math.max(0.125, xScale));
yScale = Math.min(8, Math.max(0.125, yScale));


var didZoom = false;
if (context.touchDirections.x) {
var oldDateWindow = g.dateWindow_ || context.initialRange.x
g.dateWindow_ = [
c_init.dataX - swipe.dataX + (context.initialRange.x[0] - c_init.dataX) / xScale,
c_init.dataX - swipe.dataX + (context.initialRange.x[1] - c_init.dataX) / xScale
c_init.dataX - swipe.dataX / xScale + (context.initialRange.x[0] - c_init.dataX) / xScale,
c_init.dataX - swipe.dataX / xScale + (context.initialRange.x[1] - c_init.dataX) / xScale
];
if (xExtremes[0] < xExtremes[1]) {
var pef = g.getNumericOption("panEdgeFraction") || 1/10
var a = xExtremes[0] - (xExtremes[1] - xExtremes[0]) * pef
var b = xExtremes[1] + (xExtremes[1] - xExtremes[0]) * pef
if (g.dateWindow_[0] < a) {
g.dateWindow_[0] = a
if (xScale == 1) // if it is a pan, do not scale the window
g.dateWindow_[1] = oldDateWindow[1]
}
if (g.dateWindow_[1] > b) {
g.dateWindow_[1] = b
if (xScale == 1)
g.dateWindow_[0] = oldDateWindow[0]
}
}
didZoom = true;
}

Expand All @@ -544,10 +602,27 @@ DygraphInteraction.moveTouch = function(event, g, context) {
if (logscale) {
// TODO(danvk): implement
} else {
var oldValueRange = axis.valueRange || context.initialRange.y
axis.valueRange = [
c_init.dataY - swipe.dataY + (context.initialRange.y[0] - c_init.dataY) / yScale,
c_init.dataY - swipe.dataY + (context.initialRange.y[1] - c_init.dataY) / yScale
c_init.dataY - swipe.dataY / yScale + (context.initialRange.y[0] - c_init.dataY) / yScale,
c_init.dataY - swipe.dataY / yScale + (context.initialRange.y[1] - c_init.dataY) / yScale
];
if (yExtremes[i][0] < yExtremes[i][1]) {
var pef = g.getNumericOption("panEdgeFraction") || 1/10
var a = yExtremes[i][0] - (yExtremes[i][1] - yExtremes[i][0]) * pef
var b = yExtremes[i][1] + (yExtremes[i][1] - yExtremes[i][0]) * pef
if (axis.valueRange[0] < a) {
axis.valueRange[0] = a
if (xScale == 1) // if it is a pan, do not scale
axis.valueRange[1] = oldValueRange[1]
}
if (axis.valueRange[1] > b) {
axis.valueRange[1] = b
if (xScale == 1)
axis.valueRange[0] = oldValueRange[0]
}
}

didZoom = true;
}
}
Expand Down