@@ -24,6 +24,7 @@ var Registry = require('../../registry');
2424
2525var helpers = require ( './helpers' ) ;
2626var constants = require ( './constants' ) ;
27+ var getTraceColor = require ( '../../traces/scatter/get_trace_color' ) ;
2728
2829// hover labels for multiple horizontal bars get tilted by some angle,
2930// then need to be offset differently if they overlap
@@ -234,8 +235,8 @@ function _hover(gd, evt, subplot, noHoverEvent) {
234235 pointData ,
235236 closedataPreviousLength ,
236237
237- // closestPoints : the set of candidate points we've found to draw spikes to
238- closestPoints = {
238+ // spikePoints : the set of candidate points we've found to draw spikes to
239+ spikePoints = {
239240 hLinePoint : null ,
240241 vLinePoint : null
241242 } ;
@@ -331,8 +332,8 @@ function _hover(gd, evt, subplot, noHoverEvent) {
331332 // within one trace mode can sometimes be overridden
332333 mode = hovermode ;
333334
334- var hoverdistance = fullLayout . hoverdistance ? fullLayout . hoverdistance === - 1 ? Infinity : fullLayout . hoverdistance : constants . MAXDIST ;
335- var spikedistance = fullLayout . spikedistance ? fullLayout . spikedistance === - 1 ? Infinity : fullLayout . spikedistance : hoverdistance ;
335+ var hoverdistance = fullLayout . hoverdistance ? fullLayout . hoverdistance === 0 ? Infinity : fullLayout . hoverdistance : constants . MAXDIST ;
336+ var spikedistance = fullLayout . spikedistance ? fullLayout . spikedistance === 0 ? Infinity : fullLayout . spikedistance : hoverdistance ;
336337
337338 // container for new point, also used to pass info into module.hoverPoints
338339 pointData = {
@@ -391,11 +392,11 @@ function _hover(gd, evt, subplot, noHoverEvent) {
391392 // Find the points for the spikes first to avoid overwriting the hoverLabels data.
392393 if ( fullLayout . _has ( 'cartesian' ) ) {
393394 if ( fullLayout . hovermode === 'closest' ) {
394- closestPoints . hLinePoint = findClosestPoint ( pointData , xval , yval , 'closest' , closestPoints . hLinePoint , spikedistance ) ;
395- closestPoints . vLinePoint = findClosestPoint ( pointData , xval , yval , 'closest' , closestPoints . vLinePoint , spikedistance ) ;
395+ spikePoints . hLinePoint = setClosestPoint ( pointData , xval , yval , 'closest' , spikePoints . hLinePoint , spikedistance , 'h' ) ;
396+ spikePoints . vLinePoint = setClosestPoint ( pointData , xval , yval , 'closest' , spikePoints . vLinePoint , spikedistance , 'v' ) ;
396397 } else {
397- closestPoints . hLinePoint = findClosestPoint ( pointData , xval , yval , 'y' , closestPoints . hLinePoint , spikedistance ) ;
398- closestPoints . vLinePoint = findClosestPoint ( pointData , xval , yval , 'x' , closestPoints . vLinePoint , spikedistance ) ;
398+ spikePoints . hLinePoint = setClosestPoint ( pointData , xval , yval , 'y' , spikePoints . hLinePoint , spikedistance , 'h' ) ;
399+ spikePoints . vLinePoint = setClosestPoint ( pointData , xval , yval , 'x' , spikePoints . vLinePoint , spikedistance , 'v' ) ;
399400 }
400401 }
401402
@@ -424,21 +425,72 @@ function _hover(gd, evt, subplot, noHoverEvent) {
424425 }
425426 }
426427
427- function findClosestPoint ( pointData , xval , yval , mode , endPoint , spikedistance ) {
428+ function findClosestPoints ( pointData , xval , yval , hovermode ) {
429+ var cd = pointData . cd ,
430+ trace = cd [ 0 ] . trace ,
431+ xa = pointData . xa ,
432+ ya = pointData . ya ,
433+ xpx = xa . c2p ( xval ) ,
434+ ypx = ya . c2p ( yval ) ,
435+ hoveron = trace . hoveron || '' ;
436+
437+ if ( hoveron . indexOf ( 'points' ) !== - 1 ) {
438+ var dx = function ( di ) {
439+ // scatter points: d.mrc is the calculated marker radius
440+ // adjust the distance so if you're inside the marker it
441+ // always will show up regardless of point size, but
442+ // prioritize smaller points
443+ var rad = Math . max ( 3 , di . mrc || 0 ) ;
444+ return Math . max ( Math . abs ( xa . c2p ( di . x ) - xpx ) - rad , 1 - 3 / rad ) ;
445+ } ,
446+ dy = function ( di ) {
447+ var rad = Math . max ( 3 , di . mrc || 0 ) ;
448+ return Math . max ( Math . abs ( ya . c2p ( di . y ) - ypx ) - rad , 1 - 3 / rad ) ;
449+ } ,
450+ dxy = function ( di ) {
451+ var rad = Math . max ( 3 , di . mrc || 0 ) ,
452+ dx = xa . c2p ( di . x ) - xpx ,
453+ dy = ya . c2p ( di . y ) - ypx ;
454+ return Math . max ( Math . sqrt ( dx * dx + dy * dy ) - rad , 1 - 3 / rad ) ;
455+ } ,
456+ distfn = helpers . getDistanceFunction ( hovermode , dx , dy , dxy ) ;
457+
458+ helpers . getClosest ( cd , distfn , pointData ) ;
459+
460+ // skip the rest (for this trace) if we didn't find a close point
461+ if ( pointData . index !== false ) {
462+
463+ // the closest data point
464+ var di = cd [ pointData . index ] ,
465+ xc = xa . c2p ( di . x , true ) ,
466+ yc = ya . c2p ( di . y , true ) ,
467+ rad = di . mrc || 1 ;
468+
469+ Lib . extendFlat ( pointData , {
470+ color : getTraceColor ( trace , di ) ,
471+ x0 : xc - rad ,
472+ x1 : xc + rad ,
473+ y0 : yc - rad ,
474+ y1 : yc + rad ,
475+ } ) ;
476+ return [ pointData ] ;
477+ }
478+ }
479+ }
480+
481+ function setClosestPoint ( pointData , xval , yval , mode , endPoint , spikedistance , type ) {
428482 var tmpDistance = pointData . distance ;
429483 var tmpIndex = pointData . index ;
430484 var resultPoint = endPoint ;
431485 pointData . distance = spikedistance ;
432486 pointData . index = false ;
433- var closestPoints = trace . _module . hoverPoints ( pointData , xval , yval , mode ) ;
487+ var closestPoints = findClosestPoints ( pointData , xval , yval , mode ) ;
434488 if ( closestPoints ) {
435489 closestPoints = closestPoints . filter ( function ( point ) {
436- if ( mode === 'x ' ) {
490+ if ( type === 'v ' ) {
437491 return point . xa . showspikes ;
438- } else if ( mode === 'y ' ) {
492+ } else if ( type === 'h ' ) {
439493 return point . ya . showspikes ;
440- } else if ( mode === 'closest' ) {
441- return point . ya . showspikes && point . ya . showspikes ;
442494 }
443495 } ) ;
444496 if ( closestPoints . length ) {
@@ -477,24 +529,24 @@ function _hover(gd, evt, subplot, noHoverEvent) {
477529 } ;
478530 var oldspikepoints = gd . _spikepoints ,
479531 newspikepoints = {
480- vLinePoint : closestPoints . vLinePoint ,
481- hLinePoint : closestPoints . hLinePoint
532+ vLinePoint : spikePoints . vLinePoint ,
533+ hLinePoint : spikePoints . hLinePoint
482534 } ;
483535 gd . _spikepoints = newspikepoints ;
484536
485537 if ( hoverData . length === 0 ) {
486538 var result = dragElement . unhoverRaw ( gd , evt ) ;
487- if ( fullLayout . _has ( 'cartesian' ) && ( ( closestPoints . hLinePoint !== null ) || ( closestPoints . vLinePoint !== null ) ) ) {
539+ if ( fullLayout . _has ( 'cartesian' ) && ( ( spikePoints . hLinePoint !== null ) || ( spikePoints . vLinePoint !== null ) ) ) {
488540 if ( spikesChanged ( oldspikepoints ) ) {
489- createSpikelines2 ( closestPoints , spikelineOpts ) ;
541+ createSpikelines ( spikePoints , spikelineOpts ) ;
490542 }
491543 }
492544 return result ;
493545 }
494546
495547 if ( fullLayout . _has ( 'cartesian' ) ) {
496548 if ( spikesChanged ( oldspikepoints ) ) {
497- createSpikelines2 ( closestPoints , spikelineOpts ) ;
549+ createSpikelines ( spikePoints , spikelineOpts ) ;
498550 }
499551 }
500552
@@ -1170,17 +1222,13 @@ function cleanPoint(d, hovermode) {
11701222 return d ;
11711223}
11721224
1173- function createSpikelines2 ( closestPoints , opts ) {
1225+ function createSpikelines ( closestPoints , opts ) {
11741226 var hovermode = opts . hovermode ;
11751227 var container = opts . container ;
11761228 var fullLayout = opts . fullLayout ;
11771229 var evt = opts . event ;
1178- var hLinePoint ,
1179- vLinePoint ,
1180- xa ,
1181- ya ,
1182- hLinePointY ,
1183- vLinePointX ;
1230+ var xa ,
1231+ ya ;
11841232
11851233 var showY = closestPoints . hLinePoint ? true : false ;
11861234 var showX = closestPoints . vLinePoint ? true : false ;
@@ -1190,31 +1238,22 @@ function createSpikelines2(closestPoints, opts) {
11901238
11911239 if ( ! ( showX || showY ) ) return ;
11921240
1241+ var contrastColor = Color . combine ( fullLayout . plot_bgcolor , fullLayout . paper_bgcolor ) ;
1242+
1243+ // Horizontal line (to y-axis)
11931244 if ( showY ) {
1194- hLinePoint = closestPoints . hLinePoint ;
1245+ var hLinePoint = closestPoints . hLinePoint ;
1246+ xa = hLinePoint && hLinePoint . xa ;
11951247 ya = hLinePoint && hLinePoint . ya ;
1196- var ySnap = ya . spikesnap ;
1248+ var ySnap = ya . spikesnap ,
1249+ hLinePointX ,
1250+ hLinePointY ;
11971251 if ( ySnap === 'cursor' ) {
11981252 hLinePointY = evt . offsetY ;
11991253 } else {
12001254 hLinePointY = ya . _offset + ( hLinePoint . y0 + hLinePoint . y1 ) / 2 ;
12011255 }
1202- }
1203- if ( showX ) {
1204- vLinePoint = closestPoints . vLinePoint ;
1205- xa = vLinePoint && vLinePoint . xa ;
1206- var xSnap = xa . spikesnap ;
1207- if ( xSnap === 'cursor' ) {
1208- vLinePointX = evt . offsetX ;
1209- } else {
1210- vLinePointX = xa . _offset + ( vLinePoint . x0 + vLinePoint . x1 ) / 2 ;
1211- }
1212- }
1213-
1214- var contrastColor = Color . combine ( fullLayout . plot_bgcolor , fullLayout . paper_bgcolor ) ;
1215-
1216- // Horizontal line (to y-axis)
1217- if ( showY ) {
1256+ hLinePointX = xa . _offset + ( hLinePoint . x0 + hLinePoint . x1 ) / 2 ;
12181257 var dfltHLineColor = tinycolor . readability ( hLinePoint . color , contrastColor ) < 1.5 ?
12191258 Color . contrast ( contrastColor ) : hLinePoint . color ;
12201259 var yMode = ya . spikemode ;
@@ -1224,14 +1263,14 @@ function createSpikelines2(closestPoints, opts) {
12241263 var yThickness = ya . spikethickness ;
12251264 var yColor = ya . spikecolor || dfltHLineColor ;
12261265 var yBB = ya . _boundingBox ;
1227- var xEdge = ( ( yBB . left + yBB . right ) / 2 ) < vLinePointX ? yBB . right : yBB . left ;
1266+ var xEdge = ( ( yBB . left + yBB . right ) / 2 ) < hLinePointX ? yBB . right : yBB . left ;
12281267 var xBase ;
12291268 var xEndSpike ;
12301269
12311270 if ( yMode . indexOf ( 'toaxis' ) !== - 1 || yMode . indexOf ( 'across' ) !== - 1 ) {
12321271 if ( yMode . indexOf ( 'toaxis' ) !== - 1 ) {
12331272 xBase = xEdge ;
1234- xEndSpike = vLinePointX ;
1273+ xEndSpike = hLinePointX ;
12351274 }
12361275 if ( yMode . indexOf ( 'across' ) !== - 1 ) {
12371276 xBase = ya . _counterSpan [ 0 ] ;
@@ -1279,24 +1318,36 @@ function createSpikelines2(closestPoints, opts) {
12791318 }
12801319
12811320 if ( showX ) {
1321+ var vLinePoint = closestPoints . vLinePoint ;
1322+ xa = vLinePoint && vLinePoint . xa ;
1323+ ya = vLinePoint && vLinePoint . ya ;
1324+ var xSnap = xa . spikesnap ,
1325+ vLinePointX ,
1326+ vLinePointY ;
1327+ if ( xSnap === 'cursor' ) {
1328+ vLinePointX = evt . offsetX ;
1329+ } else {
1330+ vLinePointX = xa . _offset + ( vLinePoint . x0 + vLinePoint . x1 ) / 2 ;
1331+ }
1332+ vLinePointY = ya . _offset + ( vLinePoint . y0 + vLinePoint . y1 ) / 2 ;
12821333 var dfltVLineColor = tinycolor . readability ( vLinePoint . color , contrastColor ) < 1.5 ?
1283- Color . contrast ( contrastColor ) : vLinePoint . color ;
1334+ Color . contrast ( contrastColor ) : vLinePoint . color ;
12841335 var xMode = xa . spikemode ;
12851336 if ( hovermode !== 'closest' && xMode . indexOf ( 'toaxis' ) !== - 1 ) {
12861337 xMode = xMode . replace ( 'toaxis' , 'across' ) ;
12871338 }
12881339 var xThickness = xa . spikethickness ;
12891340 var xColor = xa . spikecolor || dfltVLineColor ;
12901341 var xBB = xa . _boundingBox ;
1291- var yEdge = ( ( xBB . top + xBB . bottom ) / 2 ) < hLinePointY ? xBB . bottom : xBB . top ;
1342+ var yEdge = ( ( xBB . top + xBB . bottom ) / 2 ) < vLinePointY ? xBB . bottom : xBB . top ;
12921343
12931344 var yBase ;
12941345 var yEndSpike ;
12951346
12961347 if ( xMode . indexOf ( 'toaxis' ) !== - 1 || xMode . indexOf ( 'across' ) !== - 1 ) {
12971348 if ( xMode . indexOf ( 'toaxis' ) !== - 1 ) {
12981349 yBase = yEdge ;
1299- yEndSpike = hLinePointY ;
1350+ yEndSpike = vLinePointY ;
13001351 }
13011352 if ( xMode . indexOf ( 'across' ) !== - 1 ) {
13021353 yBase = xa . _counterSpan [ 0 ] ;
0 commit comments