Skip to content
67 changes: 47 additions & 20 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ var YSHIFTY = Math.sin(YA_RADIANS);
var HOVERARROWSIZE = constants.HOVERARROWSIZE;
var HOVERTEXTPAD = constants.HOVERTEXTPAD;

var multipleHoverPoints = {
box: true,
ohlc: true,
violin: true,
candlestick: true
};

// fx.hover: highlight data on hover
// evt can be a mousemove event, or an object with data about what points
// to hover on
Expand Down Expand Up @@ -651,39 +658,58 @@ function _hover(gd, evt, subplot, noHoverEvent) {
};
sortHoverData();

// If in compare mode, select every point at position
if(
helpers.isXYhover(_mode) &&
hoverData[0].length !== 0 &&
hoverData[0].trace.type !== 'splom' // TODO: add support for splom
) {
// pick winning point
var winningPoint = hoverData[0];
// discard other points
if(multipleHoverPoints[winningPoint.trace.type]) {
hoverData = hoverData.filter(function(d) {
return d.trace.index === winningPoint.trace.index;
});
} else {
hoverData = [winningPoint];
}
var initLen = hoverData.length;

var customXVal = customVal('x', winningPoint, fullLayout);
var customYVal = customVal('y', winningPoint, fullLayout);
var winX = getCoord('x', winningPoint, fullLayout);
var winY = getCoord('y', winningPoint, fullLayout);

findHoverPoints(customXVal, customYVal);
// in compare mode, select every point at position
findHoverPoints(winX, winY);

var finalPoints = [];
var seen = {};
var insert = function(hd) {
var type = hd.trace.type;
var key = (
type === 'box' ||
type === 'violin' ||
type === 'ohlc' ||
type === 'candlestick'
) ? hoverDataKey(hd) : hd.trace.index;
var id = 0;
var insert = function(newHd) {
var key = multipleHoverPoints[newHd.trace.type] ? hoverDataKey(newHd) : newHd.trace.index;
if(!seen[key]) {
seen[key] = true;
finalPoints.push(hd);
id++;
seen[key] = id;
finalPoints.push(newHd);
} else {
var oldId = seen[key] - 1;
var oldHd = finalPoints[oldId];
if(oldId > 0 &&
Math.abs(newHd.distance) <
Math.abs(oldHd.distance)
) {
// replace with closest
finalPoints[oldId] = newHd;
}
}
};

// insert the winnig point first
insert(winningPoint);
var k;
// insert the winnig point(s) first
for(k = 0; k < initLen; k++) {
insert(hoverData[k]);
}
// override from the end
for(var k = hoverData.length - 1; k > 0; k--) {
for(k = hoverData.length - 1; k > initLen - 1; k--) {
insert(hoverData[k]);
}
hoverData = finalPoints;
Expand Down Expand Up @@ -1045,8 +1071,9 @@ function createHoverText(hoverData, opts, gd) {
legendDraw(gd, mockLegend);

// Position the hover
var ly = Lib.mean(hoverData.map(function(c) {return (c.y0 + c.y1) / 2;}));
var lx = Lib.mean(hoverData.map(function(c) {return (c.x0 + c.x1) / 2;}));
var winningPoint = hoverData[0];
var ly = (winningPoint.y0 + winningPoint.y1) / 2;
var lx = (winningPoint.x0 + winningPoint.x1) / 2;
var legendContainer = container.select('g.legend');
var tbb = legendContainer.node().getBoundingClientRect();
lx += xa._offset;
Expand Down Expand Up @@ -1892,7 +1919,7 @@ function orderRangePoints(hoverData, hovermode) {
return first.concat(second).concat(last);
}

function customVal(axLetter, winningPoint, fullLayout) {
function getCoord(axLetter, winningPoint, fullLayout) {
var ax = winningPoint[axLetter + 'a'];
var val = winningPoint[axLetter + 'Val'];

Expand Down
43 changes: 24 additions & 19 deletions src/traces/bar/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,35 @@ function hoverOnBars(pointData, xval, yval, hovermode, opts) {

var posVal, sizeVal, posLetter, sizeLetter, dx, dy, pRangeCalc;

if(trace.orientation === 'h') {
posVal = yval;
sizeVal = xval;
posLetter = 'y';
sizeLetter = 'x';
dx = sizeFn;
dy = positionFn;
} else {
posVal = xval;
sizeVal = yval;
posLetter = 'x';
sizeLetter = 'y';
dy = sizeFn;
dx = positionFn;
}

var period = trace[posLetter + 'period'];

function thisBarMinPos(di) { return thisBarExtPos(di, -1); }
function thisBarMaxPos(di) { return thisBarExtPos(di, 1); }

function thisBarExtPos(di, sgn) {
return di[posLetter] + 0.5 * sgn * di.w;
if(period) {
return di.p + sgn * Math.abs(di.p - di.orig_p);
}
return di[posLetter] + sgn * di.w / 2;
}

var minPos = isClosest ?
var minPos = isClosest || period ?
thisBarMinPos :
function(di) {
/*
Expand All @@ -60,7 +81,7 @@ function hoverOnBars(pointData, xval, yval, hovermode, opts) {
return Math.min(thisBarMinPos(di), di.p - t.bardelta / 2);
};

var maxPos = isClosest ?
var maxPos = isClosest || period ?
thisBarMaxPos :
function(di) {
return Math.max(thisBarMaxPos(di), di.p + t.bardelta / 2);
Expand Down Expand Up @@ -118,22 +139,6 @@ function hoverOnBars(pointData, xval, yval, hovermode, opts) {
return Fx.inbox(b - v, s - v, maxSpikeDistance + (s - v) / (s - b) - 1);
}

if(trace.orientation === 'h') {
posVal = yval;
sizeVal = xval;
posLetter = 'y';
sizeLetter = 'x';
dx = sizeFn;
dy = positionFn;
} else {
posVal = xval;
sizeVal = yval;
posLetter = 'x';
sizeLetter = 'y';
dy = sizeFn;
dx = positionFn;
}

var pa = pointData[posLetter + 'a'];
var sa = pointData[sizeLetter + 'a'];

Expand Down
5 changes: 3 additions & 2 deletions src/traces/scatter/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
var rad = Math.max(3, di.mrc || 0);
var kink = 1 - 1 / rad;
var dxRaw = Math.abs(xa.c2p(di.x) - xpx);
var d = (dxRaw < rad) ? (kink * dxRaw / rad) : (dxRaw - rad + kink);
return d;
if(di.orig_x !== undefined) dxRaw += xa.c2p(di.orig_x) - xa.c2p(di.x);
return (dxRaw < rad) ? (kink * dxRaw / rad) : (dxRaw - rad + kink);
};
var dy = function(di) {
var rad = Math.max(3, di.mrc || 0);
var kink = 1 - 1 / rad;
var dyRaw = Math.abs(ya.c2p(di.y) - ypx);
if(di.orig_y !== undefined) dyRaw += ya.c2p(di.orig_y) - ya.c2p(di.y);
return (dyRaw < rad) ? (kink * dyRaw / rad) : (dyRaw - rad + kink);
};
var dxy = function(di) {
Expand Down
2 changes: 2 additions & 0 deletions src/traces/scattergl/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ function hoverPoints(pointData, xval, yval, hovermode) {
for(i = 0; i < ids.length; i++) {
ptx = x[ids[i]];
dx = Math.abs(xa.c2p(ptx) - xpx);
if(trace._origX && trace._origX[i] !== undefined) dx += xa.c2p(trace._origX[i]) - xa.c2p(ptx);
if(dx < minDist) {
minDist = dx;
dy = ya.c2p(y[ids[i]]) - ypx;
if(trace._origY && trace._origY[i] !== undefined) dy += ya.c2p(trace._origY[i]) - ya.c2p(pty);
dxy = Math.sqrt(dx * dx + dy * dy);
id = ids[i];
}
Expand Down
Loading