From 12f199a773a7685d64bad3ddd7cd3bed924ee361 Mon Sep 17 00:00:00 2001 From: archmoj Date: Tue, 25 May 2021 12:15:08 -0400 Subject: [PATCH 01/10] fix and improve closest pick in compare modes --- src/components/fx/hover.js | 19 +++- test/jasmine/tests/hover_label_test.js | 126 ++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 6 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 947285096dd..2d7593e02f7 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -666,17 +666,30 @@ function _hover(gd, evt, subplot, noHoverEvent) { var finalPoints = []; var seen = {}; + var id = 0; var insert = function(hd) { var type = hd.trace.type; - var key = ( + var multiplePoints = ( type === 'box' || type === 'violin' || type === 'ohlc' || type === 'candlestick' - ) ? hoverDataKey(hd) : hd.trace.index; + ); + + var key = multiplePoints ? hoverDataKey(hd) : hd.trace.index; if(!seen[key]) { - seen[key] = true; + id++; + seen[key] = id; finalPoints.push(hd); + } else { + var oldId = seen[key] - 1; + if( + Math.abs(winningPoint.distance - hd.distance) < + Math.abs(winningPoint.distance - finalPoints[oldId].distance) + ) { + // replace with closest + finalPoints[oldId] = hd; + } } }; diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index 9db86bcb2d5..ab92581688e 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -5319,6 +5319,126 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); + it('period with hover distance -1 include closest not farthest', function(done) { + Plotly.newPlot(gd, { + data: [ + { + name: 'bar', + type: 'bar', + x: [ + '2017-07-01', + '2017-10-01', + '2018-01-01', + ], + xhoverformat: 'Q%q', + xperiod: 'M3', + y: [ + 12, + 15, + 18 + ] + }, + { + name: 'scatter', + type: 'scatter', + x: [ + '2017-07-01', + '2017-08-01', + '2017-09-01', + '2017-10-01', + '2017-11-01', + '2017-12-01', + '2018-01-01', + '2018-02-01', + '2018-03-01', + ], + xhoverformat: '%b', + xperiod: 'M1', + y: [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + ] + } + ], + layout: { + showlegend: false, + width: 600, + height: 400, + hovermode: 'x unified', + hoverdistance: -1, + xaxis: { + dtick: 'M1', + showgrid: true, + ticklabelmode: 'period', + type: 'date' + } + } + }) + .then(function(gd) { + _hover(gd, { xpx: 50, ypx: 200 }); + assertLabel({title: 'Jul', items: [ + 'bar : (Q3, 12)', + 'scatter : 1' + ]}); + + _hover(gd, { xpx: 75, ypx: 200 }); + assertLabel({title: 'Aug', items: [ + 'bar : (Q3, 12)', + 'scatter : 2' + ]}); + + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({title: 'Sep', items: [ + 'bar : (Q3, 12)', + 'scatter : 3' + ]}); + + _hover(gd, { xpx: 150, ypx: 200 }); + assertLabel({title: 'Oct', items: [ + 'bar : (Q4, 15)', + 'scatter : 4' + ]}); + + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({title: 'Nov', items: [ + 'bar : (Q4, 15)', + 'scatter : 5' + ]}); + + _hover(gd, { xpx: 250, ypx: 200 }); + assertLabel({title: 'Dec', items: [ + 'bar : (Q4, 15)', + 'scatter : 6' + ]}); + + _hover(gd, { xpx: 300, ypx: 200 }); + assertLabel({title: 'Jan', items: [ + 'bar : (Q1, 18)', + 'scatter : 7' + ]}); + + _hover(gd, { xpx: 350, ypx: 200 }); + assertLabel({title: 'Feb', items: [ + 'bar : (Q1, 18)', + 'scatter : 8' + ]}); + + _hover(gd, { xpx: 400, ypx: 200 }); + assertLabel({title: 'Mar', items: [ + 'bar : (Q1, 18)', + 'scatter : 9' + ]}); + }) + .then(done, done.fail); + }); + it('should have the same traceorder as the legend', function(done) { var mock = require('@mocks/stacked_area.json'); var mockCopy = Lib.extendDeep({}, mock); @@ -5347,10 +5467,10 @@ describe('hovermode: (x|y)unified', function() { .then(function(gd) { _hover(gd, {curveNumber: 0}); - assertLabel({title: 'Apr 13, 2014, 15:21:15', items: [ + assertLabel({title: 'Apr 13, 2014, 15:21:11', items: [ 'Outdoor (wun... : (Apr 13, 2014, 15:26:12, 69.4)', - '1st Floor (N... : 74.8', - '2nd Floor (R... : (Apr 13, 2014, 15:21:11, 73.625)', + '1st Floor (N... : (Apr 13, 2014, 15:21:15, 74.8)', + '2nd Floor (R... : 73.625', 'Attic (Ardui... : (Apr 13, 2014, 15:26:34, 98.49)' ]}); }) From 756f49881cdcb2c1e09ada39b56bdcd05bddc887 Mon Sep 17 00:00:00 2001 From: archmoj Date: Tue, 25 May 2021 16:30:35 -0400 Subject: [PATCH 02/10] refactor insert function in hover.js --- src/components/fx/hover.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 2d7593e02f7..4f99d03feeb 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -667,8 +667,8 @@ function _hover(gd, evt, subplot, noHoverEvent) { var finalPoints = []; var seen = {}; var id = 0; - var insert = function(hd) { - var type = hd.trace.type; + var insert = function(newHd) { + var type = newHd.trace.type; var multiplePoints = ( type === 'box' || type === 'violin' || @@ -676,19 +676,20 @@ function _hover(gd, evt, subplot, noHoverEvent) { type === 'candlestick' ); - var key = multiplePoints ? hoverDataKey(hd) : hd.trace.index; + var key = multiplePoints ? hoverDataKey(newHd) : newHd.trace.index; if(!seen[key]) { id++; seen[key] = id; - finalPoints.push(hd); + finalPoints.push(newHd); } else { var oldId = seen[key] - 1; + var oldHd = finalPoints[oldId]; if( - Math.abs(winningPoint.distance - hd.distance) < - Math.abs(winningPoint.distance - finalPoints[oldId].distance) + Math.abs(winningPoint.distance - newHd.distance) < + Math.abs(winningPoint.distance - oldHd.distance) ) { // replace with closest - finalPoints[oldId] = hd; + finalPoints[oldId] = newHd; } } }; From 391074b0d3f4eda2bbc8acf009235ee5c4e6bf5c Mon Sep 17 00:00:00 2001 From: archmoj Date: Tue, 25 May 2021 16:58:36 -0400 Subject: [PATCH 03/10] fixup hover on period bars - use period width --- src/traces/bar/hover.js | 9 +- test/jasmine/tests/hover_label_test.js | 134 ++++++++++++++----------- 2 files changed, 83 insertions(+), 60 deletions(-) diff --git a/src/traces/bar/hover.js b/src/traces/bar/hover.js index 2c0ba4d86ba..f5fa3da99fb 100644 --- a/src/traces/bar/hover.js +++ b/src/traces/bar/hover.js @@ -39,10 +39,13 @@ function hoverOnBars(pointData, xval, yval, hovermode, opts) { function thisBarMaxPos(di) { return thisBarExtPos(di, 1); } function thisBarExtPos(di, sgn) { - return di[posLetter] + 0.5 * sgn * di.w; + if(di.orig_p !== undefined) { + return di.p + sgn * Math.abs(di.p - di.orig_p); + } + return di[posLetter] + sgn * di.w / 2; } - var minPos = isClosest ? + var minPos = isClosest || trace[posLetter + 'period'] ? thisBarMinPos : function(di) { /* @@ -60,7 +63,7 @@ function hoverOnBars(pointData, xval, yval, hovermode, opts) { return Math.min(thisBarMinPos(di), di.p - t.bardelta / 2); }; - var maxPos = isClosest ? + var maxPos = isClosest || trace[posLetter + 'period'] ? thisBarMaxPos : function(di) { return Math.max(thisBarMaxPos(di), di.p + t.bardelta / 2); diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index ab92581688e..add62d8211b 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -2967,13 +2967,6 @@ describe('hover on traces with (x|y)period positioning', function() { nums: '(Q1, 1)' }); }) - .then(function() { _hover(380, 395); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (M3)', - nums: '(Q1, 2)' - }); - }) .then(function() { _hover(415, 425); }) .then(function() { assertHoverLabelContent({ @@ -3010,13 +3003,6 @@ describe('hover on traces with (x|y)period positioning', function() { nums: '(Jan 2001, 1)' }); }) - .then(function() { _hover(665, 395); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (M12)', - nums: '(Jul 2001, 2)' - }); - }) .then(function() { _hover(700, 425); }) .then(function() { assertHoverLabelContent({ @@ -5215,7 +5201,7 @@ describe('hovermode: (x|y)unified', function() { _hover(gd, { xpx: 100, ypx: 200 }); assertLabel({title: 'Jan 1, 2000', items: [ - 'bar : (Dec, 2)', + 'bar : (Jan, 1)', 'scatter : 1.1' ]}); @@ -5326,31 +5312,41 @@ describe('hovermode: (x|y)unified', function() { name: 'bar', type: 'bar', x: [ - '2017-07-01', - '2017-10-01', - '2018-01-01', + '2017-01', + '2017-04', + '2017-07', + '2017-10', + '2018-01', ], xhoverformat: 'Q%q', xperiod: 'M3', y: [ - 12, - 15, - 18 + 0, + 3, + 6, + 9, + 12 ] }, { name: 'scatter', type: 'scatter', x: [ - '2017-07-01', - '2017-08-01', - '2017-09-01', - '2017-10-01', - '2017-11-01', - '2017-12-01', - '2018-01-01', - '2018-02-01', - '2018-03-01', + '2017-01', + '2017-02', + '2017-03', + '2017-04', + '2017-05', + '2017-06', + '2017-07', + '2017-08', + '2017-09', + '2017-10', + '2017-11', + '2017-12', + '2018-01', + '2018-02', + '2018-03' ], xhoverformat: '%b', xperiod: 'M1', @@ -5364,6 +5360,12 @@ describe('hovermode: (x|y)unified', function() { 7, 8, 9, + 10, + 11, + 12, + 13, + 14, + 15 ] } ], @@ -5382,59 +5384,77 @@ describe('hovermode: (x|y)unified', function() { } }) .then(function(gd) { - _hover(gd, { xpx: 50, ypx: 200 }); - assertLabel({title: 'Jul', items: [ - 'bar : (Q3, 12)', + _hover(gd, { xpx: 25, ypx: 200 }); + assertLabel({title: 'Jan', items: [ + 'bar : (Q1, 0)', 'scatter : 1' ]}); - _hover(gd, { xpx: 75, ypx: 200 }); - assertLabel({title: 'Aug', items: [ - 'bar : (Q3, 12)', + _hover(gd, { xpx: 50, ypx: 200 }); + assertLabel({title: 'Feb', items: [ + 'bar : (Q1, 0)', 'scatter : 2' ]}); - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: 'Sep', items: [ - 'bar : (Q3, 12)', + _hover(gd, { xpx: 75, ypx: 200 }); + assertLabel({title: 'Mar', items: [ + 'bar : (Q1, 0)', 'scatter : 3' ]}); - _hover(gd, { xpx: 150, ypx: 200 }); - assertLabel({title: 'Oct', items: [ - 'bar : (Q4, 15)', + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({title: 'Apr', items: [ + 'bar : (Q2, 3)', 'scatter : 4' ]}); - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: 'Nov', items: [ - 'bar : (Q4, 15)', + _hover(gd, { xpx: 125, ypx: 200 }); + assertLabel({title: 'May', items: [ + 'bar : (Q2, 3)', 'scatter : 5' ]}); - _hover(gd, { xpx: 250, ypx: 200 }); - assertLabel({title: 'Dec', items: [ - 'bar : (Q4, 15)', + _hover(gd, { xpx: 150, ypx: 200 }); + assertLabel({title: 'Jun', items: [ + 'bar : (Q2, 3)', 'scatter : 6' ]}); - _hover(gd, { xpx: 300, ypx: 200 }); - assertLabel({title: 'Jan', items: [ - 'bar : (Q1, 18)', + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({title: 'Jul', items: [ + 'bar : (Q3, 6)', 'scatter : 7' ]}); - _hover(gd, { xpx: 350, ypx: 200 }); - assertLabel({title: 'Feb', items: [ - 'bar : (Q1, 18)', + _hover(gd, { xpx: 225, ypx: 200 }); + assertLabel({title: 'Aug', items: [ + 'bar : (Q3, 6)', 'scatter : 8' ]}); - _hover(gd, { xpx: 400, ypx: 200 }); - assertLabel({title: 'Mar', items: [ - 'bar : (Q1, 18)', + _hover(gd, { xpx: 250, ypx: 200 }); + assertLabel({title: 'Sep', items: [ + 'bar : (Q3, 6)', 'scatter : 9' ]}); + + _hover(gd, { xpx: 275, ypx: 200 }); + assertLabel({title: 'Oct', items: [ + 'bar : (Q4, 9)', + 'scatter : 10' + ]}); + + _hover(gd, { xpx: 300, ypx: 200 }); + assertLabel({title: 'Nov', items: [ + 'bar : (Q4, 9)', + 'scatter : 11' + ]}); + + _hover(gd, { xpx: 325, ypx: 200 }); + assertLabel({title: 'Dec', items: [ + 'bar : (Q4, 9)', + 'scatter : 12' + ]}); }) .then(done, done.fail); }); From c692edae5dbb7bccfcfc33861cb6634d191cb698 Mon Sep 17 00:00:00 2001 From: archmoj Date: Tue, 25 May 2021 19:58:17 -0400 Subject: [PATCH 04/10] use winning point position in unified hover instead of mean --- src/components/fx/hover.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 4f99d03feeb..369aeec8dca 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1059,8 +1059,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; From 71a14e95b7c5c776e321655625e64806a32b98eb Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 26 May 2021 14:21:18 -0400 Subject: [PATCH 05/10] fresh start hover points with the winning point --- src/components/fx/hover.js | 7 ++++--- test/jasmine/tests/hover_label_test.js | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 369aeec8dca..f61d535ccba 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -658,6 +658,7 @@ function _hover(gd, evt, subplot, noHoverEvent) { hoverData[0].trace.type !== 'splom' // TODO: add support for splom ) { var winningPoint = hoverData[0]; + hoverData = [winningPoint]; var customXVal = customVal('x', winningPoint, fullLayout); var customYVal = customVal('y', winningPoint, fullLayout); @@ -684,9 +685,9 @@ function _hover(gd, evt, subplot, noHoverEvent) { } else { var oldId = seen[key] - 1; var oldHd = finalPoints[oldId]; - if( - Math.abs(winningPoint.distance - newHd.distance) < - Math.abs(winningPoint.distance - oldHd.distance) + if(oldId > 0 && + Math.abs(newHd.distance) < + Math.abs(oldHd.distance) ) { // replace with closest finalPoints[oldId] = newHd; diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index add62d8211b..daab96cc959 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -5487,10 +5487,10 @@ describe('hovermode: (x|y)unified', function() { .then(function(gd) { _hover(gd, {curveNumber: 0}); - assertLabel({title: 'Apr 13, 2014, 15:21:11', items: [ + assertLabel({title: 'Apr 13, 2014, 15:21:15', items: [ 'Outdoor (wun... : (Apr 13, 2014, 15:26:12, 69.4)', - '1st Floor (N... : (Apr 13, 2014, 15:21:15, 74.8)', - '2nd Floor (R... : 73.625', + '1st Floor (N... : 74.8', + '2nd Floor (R... : (Apr 13, 2014, 15:21:11, 73.625)', 'Attic (Ardui... : (Apr 13, 2014, 15:26:34, 98.49)' ]}); }) From b240a70aa894873af22924b77d9e1fe7df494706 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 26 May 2021 14:41:44 -0400 Subject: [PATCH 06/10] refactor and add comments --- src/components/fx/hover.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index f61d535ccba..4b9f2694537 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -651,19 +651,21 @@ 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 hoverData = [winningPoint]; - 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 = {}; @@ -1908,7 +1910,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']; From fa67044b436f55d276a3e296bbc5b73cd330e26c Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 26 May 2021 15:01:22 -0400 Subject: [PATCH 07/10] init variables fixing hover error --- src/traces/bar/hover.js | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/traces/bar/hover.js b/src/traces/bar/hover.js index f5fa3da99fb..deb78362095 100644 --- a/src/traces/bar/hover.js +++ b/src/traces/bar/hover.js @@ -35,17 +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) { - if(di.orig_p !== undefined) { + if(period) { return di.p + sgn * Math.abs(di.p - di.orig_p); } return di[posLetter] + sgn * di.w / 2; } - var minPos = isClosest || trace[posLetter + 'period'] ? + var minPos = isClosest || period ? thisBarMinPos : function(di) { /* @@ -63,7 +81,7 @@ function hoverOnBars(pointData, xval, yval, hovermode, opts) { return Math.min(thisBarMinPos(di), di.p - t.bardelta / 2); }; - var maxPos = isClosest || trace[posLetter + 'period'] ? + var maxPos = isClosest || period ? thisBarMaxPos : function(di) { return Math.max(thisBarMaxPos(di), di.p + t.bardelta / 2); @@ -121,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']; From f8b584edcb798d0c42cf0efa16a9068dfae96780 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 26 May 2021 15:31:46 -0400 Subject: [PATCH 08/10] handle winning points in traces that produce multiple points --- src/components/fx/hover.js | 35 +++++++++++++++++++------------ test/jasmine/tests/violin_test.js | 4 ++-- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 4b9f2694537..57541162afa 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -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 @@ -659,7 +666,14 @@ function _hover(gd, evt, subplot, noHoverEvent) { // pick winning point var winningPoint = hoverData[0]; // discard other points - hoverData = [winningPoint]; + 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 winX = getCoord('x', winningPoint, fullLayout); var winY = getCoord('y', winningPoint, fullLayout); @@ -671,15 +685,7 @@ function _hover(gd, evt, subplot, noHoverEvent) { var seen = {}; var id = 0; var insert = function(newHd) { - var type = newHd.trace.type; - var multiplePoints = ( - type === 'box' || - type === 'violin' || - type === 'ohlc' || - type === 'candlestick' - ); - - var key = multiplePoints ? hoverDataKey(newHd) : newHd.trace.index; + var key = multipleHoverPoints[newHd.trace.type] ? hoverDataKey(newHd) : newHd.trace.index; if(!seen[key]) { id++; seen[key] = id; @@ -697,10 +703,13 @@ function _hover(gd, evt, subplot, noHoverEvent) { } }; - // 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; diff --git a/test/jasmine/tests/violin_test.js b/test/jasmine/tests/violin_test.js index a8dbd926bcf..4b111891583 100644 --- a/test/jasmine/tests/violin_test.js +++ b/test/jasmine/tests/violin_test.js @@ -537,8 +537,8 @@ describe('Test violin hover:', function() { name: ['', '', '', '', '', ''], axis: 'Sat', hoverLabelPos: [ - [364, 270], [387, 270], [339, 270], - [346, 270], [349, 270], [352, 270] + [364, 270], [352, 270], [339, 270], + [346, 270], [349, 270], [387, 270] ] }, { desc: 'single horizontal violin', From 4f92ccfddd5b1fcfaeecf638bec3f906e879c7e3 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 26 May 2021 17:30:58 -0400 Subject: [PATCH 09/10] align period scatter points - improve tests --- src/traces/scatter/hover.js | 5 +-- test/jasmine/tests/hover_label_test.js | 42 ++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js index b9dc41387c4..b4b6a62dd23 100644 --- a/src/traces/scatter/hover.js +++ b/src/traces/scatter/hover.js @@ -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) { diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index daab96cc959..ce902ac37b7 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -5154,7 +5154,7 @@ describe('hovermode: (x|y)unified', function() { xperiod: 0, desc: 'non-period scatter points and period bars' }, { - xperiod: 24 * 3600 * 1000, + xperiod: 5 * 24 * 3600 * 1000, desc: 'period scatter points and period bars' }].forEach(function(t) { it(t.desc, function(done) { @@ -5199,12 +5199,23 @@ describe('hovermode: (x|y)unified', function() { 'bar : 2' ]}); + _hover(gd, { xpx: 75, ypx: 200 }); + assertLabel({title: 'Dec', items: [ + 'bar : 2' + ]}); + _hover(gd, { xpx: 100, ypx: 200 }); assertLabel({title: 'Jan 1, 2000', items: [ 'bar : (Jan, 1)', 'scatter : 1.1' ]}); + _hover(gd, { xpx: 125, ypx: 200 }); + assertLabel({title: 'Jan 6, 2000', items: [ + 'bar : (Jan, 1)', + 'scatter : 1.2' + ]}); + _hover(gd, { xpx: 150, ypx: 200 }); assertLabel({title: 'Jan 11, 2000', items: [ 'bar : (Jan, 1)', @@ -5217,18 +5228,35 @@ describe('hovermode: (x|y)unified', function() { 'scatter : 1.6' ]}); + _hover(gd, { xpx: 225, ypx: 200 }); + assertLabel({title: 'Feb 1, 2000', items: [ + 'bar : (Feb, 3)', + 'scatter : 2.1' + ]}); + _hover(gd, { xpx: 250, ypx: 200 }); assertLabel({title: 'Feb 11, 2000', items: [ 'bar : (Feb, 3)', 'scatter : 2.3' ]}); + _hover(gd, { xpx: 275, ypx: 200 }); + assertLabel({title: 'Feb 16, 2000', items: [ + 'bar : (Feb, 3)', + 'scatter : 2.4' + ]}); + _hover(gd, { xpx: 300, ypx: 200 }); assertLabel({title: 'Feb 21, 2000', items: [ 'bar : (Feb, 3)', 'scatter : 2.5' ]}); + _hover(gd, { xpx: 325, ypx: 200 }); + assertLabel({title: 'Mar 1, 2000', items: [ + 'scatter : 3.1' + ]}); + _hover(gd, { xpx: 350, ypx: 200 }); assertLabel({title: 'Mar 6, 2000', items: [ 'scatter : 3.2' @@ -5279,20 +5307,22 @@ describe('hovermode: (x|y)unified', function() { _hover(gd, { xpx: 40, ypx: 200 }); assertLabel({title: 'Jan', items: [ 'bar : (Jan 1, 2000, 1)', - 'start : 1' + 'start : 1', + 'end : 1' ]}); _hover(gd, { xpx: 100, ypx: 200 }); assertLabel({title: 'Jan', items: [ 'bar : (Jan 1, 2000, 1)', - 'start : 1' + 'start : 1', + 'end : 1' ]}); _hover(gd, { xpx: 360, ypx: 200 }); - assertLabel({title: 'Jan', items: [ + assertLabel({title: 'Feb', items: [ 'bar : (Feb 1, 2000, 2)', - 'start : (Feb, 2)', - 'end : 1' + 'start : 2', + 'end : 2' ]}); _hover(gd, { xpx: 400, ypx: 200 }); From 282a2279c1e1d67bc54bb09c8c8d93713f3f72ea Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 26 May 2021 18:41:50 -0400 Subject: [PATCH 10/10] align period for scattergl --- src/traces/scattergl/hover.js | 2 + test/jasmine/tests/hover_label_test.js | 130 +++++++++++++------------ 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/src/traces/scattergl/hover.js b/src/traces/scattergl/hover.js index 899825c9cb0..e1b2a16ecd5 100644 --- a/src/traces/scattergl/hover.js +++ b/src/traces/scattergl/hover.js @@ -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]; } diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index ce902ac37b7..9ea0ac24305 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -5266,73 +5266,75 @@ describe('hovermode: (x|y)unified', function() { }); }); - it('period points alignments', function(done) { - Plotly.newPlot(gd, { - data: [ - { - name: 'bar', - type: 'bar', - x: ['2000-01', '2000-02'], - y: [1, 2], - xhoverfrmat: '%b', - xperiod: 'M1' - }, - { - name: 'start', - type: 'scatter', - x: ['2000-01', '2000-02'], - y: [1, 2], - xhoverformat: '%b', - xperiod: 'M1', - xperiodalignment: 'start' - }, - { - name: 'end', - type: 'scatter', - x: ['2000-01', '2000-02'], - y: [1, 2], - xhoverformat: '%b', - xperiod: 'M1', - xperiodalignment: 'end' - }, - ], - layout: { - showlegend: false, - width: 600, - height: 400, - hovermode: 'x unified' - } - }) - .then(function(gd) { - _hover(gd, { xpx: 40, ypx: 200 }); - assertLabel({title: 'Jan', items: [ - 'bar : (Jan 1, 2000, 1)', - 'start : 1', - 'end : 1' - ]}); + ['scatter', 'scattergl'].forEach(function(scatterType) { + it(scatterType + ' period points alignments', function(done) { + Plotly.newPlot(gd, { + data: [ + { + name: 'bar', + type: 'bar', + x: ['2000-01', '2000-02'], + y: [1, 2], + xhoverfrmat: '%b', + xperiod: 'M1' + }, + { + name: 'start', + type: scatterType, + x: ['2000-01', '2000-02'], + y: [1, 2], + xhoverformat: '%b', + xperiod: 'M1', + xperiodalignment: 'start' + }, + { + name: 'end', + type: 'scatter', + x: ['2000-01', '2000-02'], + y: [1, 2], + xhoverformat: '%b', + xperiod: 'M1', + xperiodalignment: 'end' + }, + ], + layout: { + showlegend: false, + width: 600, + height: 400, + hovermode: 'x unified' + } + }) + .then(function(gd) { + _hover(gd, { xpx: 40, ypx: 200 }); + assertLabel({title: 'Jan', items: [ + 'bar : (Jan 1, 2000, 1)', + 'start : 1', + 'end : 1' + ]}); - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: 'Jan', items: [ - 'bar : (Jan 1, 2000, 1)', - 'start : 1', - 'end : 1' - ]}); + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({title: 'Jan', items: [ + 'bar : (Jan 1, 2000, 1)', + 'start : 1', + 'end : 1' + ]}); - _hover(gd, { xpx: 360, ypx: 200 }); - assertLabel({title: 'Feb', items: [ - 'bar : (Feb 1, 2000, 2)', - 'start : 2', - 'end : 2' - ]}); + _hover(gd, { xpx: 360, ypx: 200 }); + assertLabel({title: 'Feb', items: [ + 'bar : (Feb 1, 2000, 2)', + 'start : 2', + 'end : 2' + ]}); - _hover(gd, { xpx: 400, ypx: 200 }); - assertLabel({title: 'Feb', items: [ - 'bar : (Feb 1, 2000, 2)', - 'start : 2', - 'end : 2' - ]}); - }) - .then(done, done.fail); + _hover(gd, { xpx: 400, ypx: 200 }); + assertLabel({title: 'Feb', items: [ + 'bar : (Feb 1, 2000, 2)', + 'start : 2', + 'end : 2' + ]}); + }) + .then(done, done.fail); + }); }); it('period with hover distance -1 include closest not farthest', function(done) {