Skip to content
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

Fix avoid-overlap hover label algo when "too close" label get filtered out #3645

Merged
merged 1 commit into from
Mar 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ function _hover(gd, evt, subplot, noHoverEvent) {

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

hoverAvoidOverlaps(hoverData, rotateLabels ? 'xa' : 'ya', fullLayout);
hoverAvoidOverlaps(hoverLabels, rotateLabels ? 'xa' : 'ya', fullLayout);

alignHoverText(hoverLabels, rotateLabels);

Expand Down Expand Up @@ -879,6 +879,7 @@ function createHoverText(hoverData, opts, gd) {

// show all the individual labels


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

// make groups of touching points
var pointgroups = hoverData.map(function(d, i) {
var pointgroups = new Array(nLabels);

hoverLabels.each(function(d, i) {
var axis = d[ax];
var axIsX = axis._id.charAt(0) === 'x';
var rng = axis.range;
if(!i && rng && ((rng[0] > rng[1]) !== axIsX)) axSign = -1;
return [{
pointgroups[i] = [{
datum: d,
i: i,
traceIndex: d.trace.index,
dp: 0,
Expand All @@ -1111,8 +1116,9 @@ function hoverAvoidOverlaps(hoverData, ax, fullLayout) {
pmin: 0,
pmax: (axIsX ? fullLayout.width : fullLayout.height)
}];
})
.sort(function(a, b) {
});

pointgroups.sort(function(a, b) {
return (a[0].posref - b[0].posref) ||
// for equal positions, sort trace indices increasing or decreasing
// depending on whether the axis is reversed or not... so stacked
Expand Down Expand Up @@ -1198,7 +1204,7 @@ function hoverAvoidOverlaps(hoverData, ax, fullLayout) {

// loop through groups, combining them if they overlap,
// until nothing moves
while(!donepositioning && nummoves <= hoverData.length) {
while(!donepositioning && nummoves <= nLabels) {
// to avoid infinite loops, don't move more times
// than there are traces
nummoves++;
Expand Down Expand Up @@ -1246,7 +1252,7 @@ function hoverAvoidOverlaps(hoverData, ax, fullLayout) {
var grp = pointgroups[i];
for(j = grp.length - 1; j >= 0; j--) {
var pt = grp[j];
var hoverPt = hoverData[pt.i];
var hoverPt = pt.datum;
hoverPt.offset = pt.dp;
hoverPt.del = pt.del;
}
Expand Down
34 changes: 34 additions & 0 deletions test/jasmine/tests/hover_label_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,40 @@ describe('hover info', function() {
.catch(failTest)
.then(done);
});

it('should avoid overlaps on *too close* pts are filtered out', function(done) {
Plotly.plot(gd, [
{name: 'A', x: [9, 10], y: [9, 10]},
{name: 'B', x: [8, 9], y: [9, 10]},
{name: 'C', x: [9, 10], y: [10, 11]}
], {
xaxis: {range: [0, 100]},
yaxis: {range: [0, 100]},
width: 700,
height: 450
})
.then(function() { _hover(gd, 67, 239); })
.then(function() {
var nodesA = hoverInfoNodes('A');
var nodesC = hoverInfoNodes('C');

// Ensure layout correct
assertLabelsInsideBoxes(nodesA, 'A');
assertLabelsInsideBoxes(nodesC, 'C');
assertSecondaryRightToPrimaryBox(nodesA, 'A');
assertSecondaryRightToPrimaryBox(nodesC, 'C');

// Ensure stacking, finally
var boxA = nodesA.primaryBox.getBoundingClientRect();
var boxC = nodesC.primaryBox.getBoundingClientRect();

// Be robust against floating point arithmetic and subtle future layout changes
expect(calcLineOverlap(boxA.top, boxA.bottom, boxC.top, boxC.bottom))
.toBeWithin(0, 1);
})
.catch(failTest)
.then(done);
});
});

describe('hovertemplate', function() {
Expand Down