Skip to content

Pick closest points to the winning point in compare mode and position unified hover box in front of winning point #5683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 28, 2021
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