Skip to content

Commit 35b3b5d

Browse files
committed
loop over hover label (not hover data) when fixing overlaps
... that way when some hover data pts are filtered out (e.g. when they are too close together in compare mode), the fix-overlap algo still works.
1 parent fc236e2 commit 35b3b5d

File tree

2 files changed

+48
-8
lines changed

2 files changed

+48
-8
lines changed

src/components/fx/hover.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ function _hover(gd, evt, subplot, noHoverEvent) {
704704

705705
var hoverLabels = createHoverText(hoverData, labelOpts, gd);
706706

707-
hoverAvoidOverlaps(hoverData, rotateLabels ? 'xa' : 'ya', fullLayout);
707+
hoverAvoidOverlaps(hoverLabels, rotateLabels ? 'xa' : 'ya', fullLayout);
708708

709709
alignHoverText(hoverLabels, rotateLabels);
710710

@@ -879,6 +879,7 @@ function createHoverText(hoverData, opts, gd) {
879879

880880
// show all the individual labels
881881

882+
882883
// first create the objects
883884
var hoverLabels = container.selectAll('g.hovertext')
884885
.data(hoverData, function(d) {
@@ -1091,17 +1092,21 @@ function createHoverText(hoverData, opts, gd) {
10911092
// know what happens if the group spans all the way from one edge to
10921093
// the other, though it hardly matters - there's just too much
10931094
// information then.
1094-
function hoverAvoidOverlaps(hoverData, ax, fullLayout) {
1095+
function hoverAvoidOverlaps(hoverLabels, ax, fullLayout) {
10951096
var nummoves = 0;
10961097
var axSign = 1;
1098+
var nLabels = hoverLabels.size();
10971099

10981100
// make groups of touching points
1099-
var pointgroups = hoverData.map(function(d, i) {
1101+
var pointgroups = new Array(nLabels);
1102+
1103+
hoverLabels.each(function(d, i) {
11001104
var axis = d[ax];
11011105
var axIsX = axis._id.charAt(0) === 'x';
11021106
var rng = axis.range;
11031107
if(!i && rng && ((rng[0] > rng[1]) !== axIsX)) axSign = -1;
1104-
return [{
1108+
pointgroups[i] = [{
1109+
datum: d,
11051110
i: i,
11061111
traceIndex: d.trace.index,
11071112
dp: 0,
@@ -1111,8 +1116,9 @@ function hoverAvoidOverlaps(hoverData, ax, fullLayout) {
11111116
pmin: 0,
11121117
pmax: (axIsX ? fullLayout.width : fullLayout.height)
11131118
}];
1114-
})
1115-
.sort(function(a, b) {
1119+
});
1120+
1121+
pointgroups.sort(function(a, b) {
11161122
return (a[0].posref - b[0].posref) ||
11171123
// for equal positions, sort trace indices increasing or decreasing
11181124
// depending on whether the axis is reversed or not... so stacked
@@ -1198,7 +1204,7 @@ function hoverAvoidOverlaps(hoverData, ax, fullLayout) {
11981204

11991205
// loop through groups, combining them if they overlap,
12001206
// until nothing moves
1201-
while(!donepositioning && nummoves <= hoverData.length) {
1207+
while(!donepositioning && nummoves <= nLabels) {
12021208
// to avoid infinite loops, don't move more times
12031209
// than there are traces
12041210
nummoves++;
@@ -1246,7 +1252,7 @@ function hoverAvoidOverlaps(hoverData, ax, fullLayout) {
12461252
var grp = pointgroups[i];
12471253
for(j = grp.length - 1; j >= 0; j--) {
12481254
var pt = grp[j];
1249-
var hoverPt = hoverData[pt.i];
1255+
var hoverPt = pt.datum;
12501256
hoverPt.offset = pt.dp;
12511257
hoverPt.del = pt.del;
12521258
}

test/jasmine/tests/hover_label_test.js

+34
Original file line numberDiff line numberDiff line change
@@ -1685,6 +1685,40 @@ describe('hover info', function() {
16851685
.catch(failTest)
16861686
.then(done);
16871687
});
1688+
1689+
it('should avoid overlaps on *too close* pts are filtered out', function(done) {
1690+
Plotly.plot(gd, [
1691+
{name: 'A', x: [9, 10], y: [9, 10]},
1692+
{name: 'B', x: [8, 9], y: [9, 10]},
1693+
{name: 'C', x: [9, 10], y: [10, 11]}
1694+
], {
1695+
xaxis: {range: [0, 100]},
1696+
yaxis: {range: [0, 100]},
1697+
width: 700,
1698+
height: 450
1699+
})
1700+
.then(function() { _hover(gd, 67, 239); })
1701+
.then(function() {
1702+
var nodesA = hoverInfoNodes('A');
1703+
var nodesC = hoverInfoNodes('C');
1704+
1705+
// Ensure layout correct
1706+
assertLabelsInsideBoxes(nodesA, 'A');
1707+
assertLabelsInsideBoxes(nodesC, 'C');
1708+
assertSecondaryRightToPrimaryBox(nodesA, 'A');
1709+
assertSecondaryRightToPrimaryBox(nodesC, 'C');
1710+
1711+
// Ensure stacking, finally
1712+
var boxA = nodesA.primaryBox.getBoundingClientRect();
1713+
var boxC = nodesC.primaryBox.getBoundingClientRect();
1714+
1715+
// Be robust against floating point arithmetic and subtle future layout changes
1716+
expect(calcLineOverlap(boxA.top, boxA.bottom, boxC.top, boxC.bottom))
1717+
.toBeWithin(0, 1);
1718+
})
1719+
.catch(failTest)
1720+
.then(done);
1721+
});
16881722
});
16891723

16901724
describe('hovertemplate', function() {

0 commit comments

Comments
 (0)