Skip to content

Commit 30bc8c7

Browse files
committed
broaden bar hover acceptance, and test
1 parent b34d9ef commit 30bc8c7

File tree

5 files changed

+97
-52
lines changed

5 files changed

+97
-52
lines changed

src/plots/cartesian/graph_interact.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,9 @@ function p2c(axArray, v) {
237237
}
238238

239239
function quadrature(dx, dy) {
240-
return function(di, i) {
241-
var x = dx(di, i),
242-
y = dy(di, i);
240+
return function(di) {
241+
var x = dx(di),
242+
y = dy(di);
243243
return Math.sqrt(x * x + y * y);
244244
};
245245
}
@@ -664,7 +664,7 @@ fx.getClosest = function(cd, distfn, pointData) {
664664
// to create pre-sorted data (by x or y), not sure how to
665665
// do this for 'closest'
666666
for(var i = 0; i < cd.length; i++) {
667-
var newDistance = distfn(cd[i], i);
667+
var newDistance = distfn(cd[i]);
668668
if(newDistance <= pointData.distance) {
669669
pointData.index = i;
670670
pointData.distance = newDistance;

src/traces/bar/hover.js

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,45 +15,57 @@ var Color = require('../../components/color');
1515

1616

1717
module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
18-
var cd = pointData.cd,
19-
trace = cd[0].trace,
20-
t = cd[0].t,
21-
xa = pointData.xa,
22-
ya = pointData.ya;
23-
24-
var barPos;
25-
if(hovermode !== 'closest') barPos = function(di) { return di.p; };
26-
else if(trace.orientation === 'h') barPos = function(di) { return di.y; };
27-
else barPos = function(di) { return di.x; };
28-
29-
var barDelta = (hovermode === 'closest') ?
30-
function(i) { return (t.barwidth[i] || t.barwidth) / 2; } :
31-
function() { return t.bargroupwidth / 2; };
32-
33-
var dx, dy;
18+
var cd = pointData.cd;
19+
var trace = cd[0].trace;
20+
var t = cd[0].t;
21+
var xa = pointData.xa;
22+
var ya = pointData.ya;
23+
24+
var posVal, thisBarMinPos, thisBarMaxPos, minPos, maxPos, dx, dy;
25+
26+
var positionFn = function(di) {
27+
return Fx.inbox(minPos(di) - posVal, maxPos(di) - posVal);
28+
};
29+
3430
if(trace.orientation === 'h') {
31+
posVal = yval;
32+
thisBarMinPos = function(di) { return di.y - di.w / 2; };
33+
thisBarMaxPos = function(di) { return di.y + di.w / 2; };
3534
dx = function(di) {
3635
// add a gradient so hovering near the end of a
3736
// bar makes it a little closer match
3837
return Fx.inbox(di.b - xval, di.x - xval) + (di.x - xval) / (di.x - di.b);
3938
};
40-
dy = function(di, i) {
41-
var centerPos = barPos(di) - yval;
42-
var delta = barDelta(i);
43-
return Fx.inbox(centerPos - delta, centerPos + delta);
44-
};
39+
dy = positionFn;
4540
}
4641
else {
42+
posVal = xval;
43+
thisBarMinPos = function(di) { return di.x - di.w / 2; };
44+
thisBarMaxPos = function(di) { return di.x + di.w / 2; };
4745
dy = function(di) {
4846
return Fx.inbox(di.b - yval, di.y - yval) + (di.y - yval) / (di.y - di.b);
4947
};
50-
dx = function(di, i) {
51-
var centerPos = barPos(di) - xval;
52-
var delta = barDelta(i);
53-
return Fx.inbox(centerPos - delta, centerPos + delta);
54-
};
48+
dx = positionFn;
5549
}
5650

51+
minPos = (hovermode === 'closest') ?
52+
thisBarMinPos :
53+
function(di) {
54+
/*
55+
* In compare mode, accept a bar if you're on it *or* its group.
56+
* Nearly always it's the group that matters, but in case the bar
57+
* was explicitly set wider than its group we'd better accept the
58+
* whole bar.
59+
*/
60+
return Math.min(thisBarMinPos(di), di.p - t.bargroupwidth / 2);
61+
};
62+
63+
maxPos = (hovermode === 'closest') ?
64+
thisBarMaxPos :
65+
function(di) {
66+
return Math.max(thisBarMaxPos(di), di.p + t.bargroupwidth / 2);
67+
};
68+
5769
var distfn = Fx.getDistanceFunction(hovermode, dx, dy);
5870
Fx.getClosest(cd, distfn, pointData);
5971

@@ -74,16 +86,16 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
7486
pointData.x0 = pointData.x1 = xa.c2p(di.x, true);
7587
pointData.xLabelVal = size;
7688

77-
pointData.y0 = ya.c2p(barPos(di) - barDelta(index), true);
78-
pointData.y1 = ya.c2p(barPos(di) + barDelta(index), true);
89+
pointData.y0 = ya.c2p(minPos(di), true);
90+
pointData.y1 = ya.c2p(maxPos(di), true);
7991
pointData.yLabelVal = di.p;
8092
}
8193
else {
8294
pointData.y0 = pointData.y1 = ya.c2p(di.y, true);
8395
pointData.yLabelVal = size;
8496

85-
pointData.x0 = xa.c2p(barPos(di) - barDelta(index), true);
86-
pointData.x1 = xa.c2p(barPos(di) + barDelta(index), true);
97+
pointData.x0 = xa.c2p(minPos(di), true);
98+
pointData.x1 = xa.c2p(maxPos(di), true);
8799
pointData.xLabelVal = di.p;
88100
}
89101

src/traces/bar/plot.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ module.exports = function plot(gd, plotinfo, cdbar) {
4848
var t = d[0].t,
4949
trace = d[0].trace,
5050
poffset = t.poffset,
51-
poffsetIsArray = Array.isArray(poffset),
52-
barwidth = t.barwidth,
53-
barwidthIsArray = Array.isArray(barwidth);
51+
poffsetIsArray = Array.isArray(poffset);
5452

5553
d3.select(this).selectAll('g.point')
5654
.data(Lib.identity)
@@ -61,7 +59,7 @@ module.exports = function plot(gd, plotinfo, cdbar) {
6159
// log values go off-screen by plotwidth
6260
// so you see them continue if you drag the plot
6361
var p0 = di.p + ((poffsetIsArray) ? poffset[i] : poffset),
64-
p1 = p0 + ((barwidthIsArray) ? barwidth[i] : barwidth),
62+
p1 = p0 + di.w,
6563
s0 = di.b,
6664
s1 = s0 + di.s;
6765

src/traces/bar/set_positions.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ function setOffsetAndWidth(gd, pa, sieve) {
236236
applyAttributes(sieve);
237237

238238
// store the bar center in each calcdata item
239-
setBarCenter(gd, pa, sieve);
239+
setBarCenterAndWidth(gd, pa, sieve);
240240

241241
// update position axes
242242
updatePositionAxis(gd, pa, sieve);
@@ -286,7 +286,7 @@ function setOffsetAndWidthInGroupMode(gd, pa, sieve) {
286286
applyAttributes(sieve);
287287

288288
// store the bar center in each calcdata item
289-
setBarCenter(gd, pa, sieve);
289+
setBarCenterAndWidth(gd, pa, sieve);
290290

291291
// update position axes
292292
updatePositionAxis(gd, pa, sieve, overlap);
@@ -377,7 +377,7 @@ function applyAttributes(sieve) {
377377
}
378378

379379

380-
function setBarCenter(gd, pa, sieve) {
380+
function setBarCenterAndWidth(gd, pa, sieve) {
381381
var calcTraces = sieve.traces,
382382
pLetter = getAxisLetter(pa);
383383

@@ -392,9 +392,11 @@ function setBarCenter(gd, pa, sieve) {
392392
for(var j = 0; j < calcTrace.length; j++) {
393393
var calcBar = calcTrace[j];
394394

395+
// store the actual bar width and position, for use by hover
396+
var width = calcBar.w = (barwidthIsArray) ? barwidth[j] : barwidth;
395397
calcBar[pLetter] = calcBar.p +
396398
((poffsetIsArray) ? poffset[j] : poffset) +
397-
((barwidthIsArray) ? barwidth[j] : barwidth) / 2;
399+
width / 2;
398400

399401

400402
}

test/jasmine/tests/bar_test.js

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var Axes = PlotlyInternal.Axes;
1010
var createGraphDiv = require('../assets/create_graph_div');
1111
var destroyGraphDiv = require('../assets/destroy_graph_div');
1212
var customMatchers = require('../assets/custom_matchers');
13+
var failTest = require('../assets/fail_test');
1314

1415
describe('Bar.supplyDefaults', function() {
1516
'use strict';
@@ -1209,7 +1210,10 @@ describe('bar hover', function() {
12091210

12101211
function _hover(gd, xval, yval, hovermode) {
12111212
var pointData = getPointData(gd);
1212-
var pt = Bar.hoverPoints(pointData, xval, yval, hovermode)[0];
1213+
var pts = Bar.hoverPoints(pointData, xval, yval, hovermode);
1214+
if(!pts) return false;
1215+
1216+
var pt = pts[0];
12131217

12141218
return {
12151219
style: [pt.index, pt.color, pt.xLabelVal, pt.yLabelVal],
@@ -1296,7 +1300,7 @@ describe('bar hover', function() {
12961300
gd = createGraphDiv();
12971301
});
12981302

1299-
it('should return correct \'closest\' hover data (single bar, trace width)', function(done) {
1303+
it('should return correct hover data (single bar, trace width)', function(done) {
13001304
Plotly.plot(gd, [{
13011305
type: 'bar',
13021306
x: [1],
@@ -1307,15 +1311,39 @@ describe('bar hover', function() {
13071311
xaxis: { range: [-200, 200] }
13081312
})
13091313
.then(function() {
1310-
var out = _hover(gd, 0, 0, 'closest');
1311-
1312-
expect(out.style).toEqual([0, 'red', 1, 2]);
1313-
assertPos(out.pos, [264, 278, 14, 14]);
1314+
// all these x, y, hovermode should give the same (the only!) hover label
1315+
[
1316+
[0, 0, 'closest'],
1317+
[-3.9, 1, 'closest'],
1318+
[5.9, 1.9, 'closest'],
1319+
[-3.9, -10, 'x'],
1320+
[5.9, 19, 'x']
1321+
].forEach(function(hoverSpec) {
1322+
var out = _hover(gd, hoverSpec[0], hoverSpec[1], hoverSpec[2]);
1323+
1324+
expect(out.style).toEqual([0, 'red', 1, 2], hoverSpec);
1325+
assertPos(out.pos, [264, 278, 14, 14], hoverSpec);
1326+
});
1327+
1328+
// then a few that are off the edge so yield nothing
1329+
[
1330+
[1, -0.1, 'closest'],
1331+
[1, 2.1, 'closest'],
1332+
[-4.1, 1, 'closest'],
1333+
[6.1, 1, 'closest'],
1334+
[-4.1, 1, 'x'],
1335+
[6.1, 1, 'x']
1336+
].forEach(function(hoverSpec) {
1337+
var out = _hover(gd, hoverSpec[0], hoverSpec[1], hoverSpec[2]);
1338+
1339+
expect(out).toBe(false, hoverSpec);
1340+
});
13141341
})
1342+
.catch(failTest)
13151343
.then(done);
13161344
});
13171345

1318-
it('should return correct \'closest\' hover data (two bars, array width)', function(done) {
1346+
it('should return correct hover data (two bars, array width)', function(done) {
13191347
Plotly.plot(gd, [{
13201348
type: 'bar',
13211349
x: [1, 200],
@@ -1338,13 +1366,18 @@ describe('bar hover', function() {
13381366

13391367
expect(out.style).toEqual([0, 'red', 1, 2]);
13401368
assertPos(out.pos, [99, 106, 13, 13]);
1341-
})
1342-
.then(function() {
1343-
var out = _hover(gd, 164, 0.8, 'closest');
1369+
1370+
out = _hover(gd, 164, 0.8, 'closest');
13441371

13451372
expect(out.style).toEqual([1, 'red', 200, 1]);
13461373
assertPos(out.pos, [222, 235, 168, 168]);
1374+
1375+
out = _hover(gd, 125, 0.8, 'x');
1376+
1377+
expect(out.style).toEqual([1, 'red', 200, 1]);
1378+
assertPos(out.pos, [201, 301, 168, 168]);
13471379
})
1380+
.catch(failTest)
13481381
.then(done);
13491382
});
13501383

0 commit comments

Comments
 (0)