@@ -24,7 +24,6 @@ var Registry = require('../../registry');
2424
2525var helpers = require ( './helpers' ) ;
2626var constants = require ( './constants' ) ;
27- var getTraceColor = require ( '../../traces/scatter/get_trace_color' ) ;
2827
2928// hover labels for multiple horizontal bars get tilted by some angle,
3029// then need to be offset differently if they overlap
@@ -145,7 +144,8 @@ exports.loneHover = function loneHover(hoverItem, opts) {
145144 rotateLabels : false ,
146145 bgColor : opts . bgColor || Color . background ,
147146 container : container3 ,
148- outerContainer : outerContainer3
147+ outerContainer : outerContainer3 ,
148+ hoverdistance : constants . MAXDIST
149149 } ;
150150
151151 var hoverLabel = createHoverText ( [ pointData ] , fullOpts , opts . gd ) ;
@@ -208,6 +208,9 @@ function _hover(gd, evt, subplot, noHoverEvent) {
208208 return dragElement . unhoverRaw ( gd , evt ) ;
209209 }
210210
211+ var hoverdistance = fullLayout . hoverdistance === 0 ? Infinity : fullLayout . hoverdistance ;
212+ var spikedistance = fullLayout . spikedistance === 0 ? Infinity : fullLayout . spikedistance ;
213+
211214 // hoverData: the set of candidate points we've found to highlight
212215 var hoverData = [ ] ,
213216
@@ -332,9 +335,6 @@ function _hover(gd, evt, subplot, noHoverEvent) {
332335 // within one trace mode can sometimes be overridden
333336 mode = hovermode ;
334337
335- var hoverdistance = fullLayout . hoverdistance === 0 ? Infinity : fullLayout . hoverdistance ;
336- var spikedistance = fullLayout . spikedistance === 0 ? Infinity : fullLayout . spikedistance ;
337-
338338 // container for new point, also used to pass info into module.hoverPoints
339339 pointData = {
340340 // trace properties
@@ -389,17 +389,6 @@ function _hover(gd, evt, subplot, noHoverEvent) {
389389 yval = yvalArray [ subploti ] ;
390390 }
391391
392- // Find the points for the spikes first to avoid overwriting the hoverLabels data.
393- if ( fullLayout . _has ( 'cartesian' ) ) {
394- if ( fullLayout . hovermode === 'closest' ) {
395- spikePoints . hLinePoint = setClosestPoint ( pointData , xval , yval , 'closest' , spikePoints . hLinePoint , spikedistance , 'h' ) ;
396- spikePoints . vLinePoint = setClosestPoint ( pointData , xval , yval , 'closest' , spikePoints . vLinePoint , spikedistance , 'v' ) ;
397- } else {
398- spikePoints . hLinePoint = setClosestPoint ( pointData , xval , yval , 'y' , spikePoints . hLinePoint , spikedistance , 'h' ) ;
399- spikePoints . vLinePoint = setClosestPoint ( pointData , xval , yval , 'x' , spikePoints . vLinePoint , spikedistance , 'v' ) ;
400- }
401- }
402-
403392 // Now find the points.
404393 if ( trace . _module && trace . _module . hoverPoints ) {
405394 var newPoints = trace . _module . hoverPoints ( pointData , xval , yval , mode , fullLayout . _hoverlayer ) ;
@@ -423,105 +412,96 @@ function _hover(gd, evt, subplot, noHoverEvent) {
423412 hoverData . splice ( 0 , closedataPreviousLength ) ;
424413 distance = hoverData [ 0 ] . distance ;
425414 }
426- }
427415
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 ] ;
416+ // Now look for the points to draw the spikelines
417+ // Do it only if there is no hoverData
418+ if ( fullLayout . _has ( 'cartesian' ) ) {
419+ if ( hoverData . length === 0 ) {
420+ pointData . distance = spikedistance ;
421+ pointData . index = false ;
422+ var closestPoints = trace . _module . hoverPoints ( pointData , xval , yval , 'closest' ) ;
423+ if ( closestPoints ) {
424+ var tmpPoint ;
425+ var closestVPoints = closestPoints . filter ( function ( point ) {
426+ return point . xa . showspikes ;
427+ } ) ;
428+ if ( closestVPoints . length ) {
429+ var closestVPt = closestVPoints [ 0 ] ;
430+ if ( isNumeric ( closestVPt . x0 ) && isNumeric ( closestVPt . y0 ) ) {
431+ tmpPoint = clearClosestPoint ( closestVPt ) ;
432+ if ( ! spikePoints . vLinePoint || ( spikePoints . vLinePoint . distance > tmpPoint . distance ) ) {
433+ spikePoints . vLinePoint = tmpPoint ;
434+ }
435+ }
436+ }
437+
438+ var closestHPoints = closestPoints . filter ( function ( point ) {
439+ return point . ya . showspikes ;
440+ } ) ;
441+ if ( closestHPoints . length ) {
442+ var closestHPt = closestHPoints [ 0 ] ;
443+ if ( isNumeric ( closestHPt . x0 ) && isNumeric ( closestHPt . y0 ) ) {
444+ tmpPoint = clearClosestPoint ( closestHPt ) ;
445+ if ( ! spikePoints . hLinePoint || ( spikePoints . hLinePoint . distance > tmpPoint . distance ) ) {
446+ spikePoints . hLinePoint = tmpPoint ;
447+ }
448+ }
449+ }
450+ }
477451 }
478452 }
479453 }
480454
481- function setClosestPoint ( pointData , xval , yval , mode , endPoint , spikedistance , type ) {
482- var tmpDistance = pointData . distance ;
483- var tmpIndex = pointData . index ;
484- var resultPoint = endPoint ;
485- pointData . distance = spikedistance ;
486- pointData . index = false ;
487- var closestPoints = findClosestPoints ( pointData , xval , yval , mode ) ;
488- if ( closestPoints ) {
489- closestPoints = closestPoints . filter ( function ( point ) {
490- if ( type === 'v' ) {
491- return point . xa . showspikes ;
492- } else if ( type === 'h' ) {
493- return point . ya . showspikes ;
494- }
455+ function selectClosestPoint ( pointsData , spikedistance ) {
456+ if ( ! pointsData . length ) return null ;
457+ var resultPoint ;
458+ var pointsDistances = pointsData . map ( function ( point , index ) {
459+ var xa = point . xa ,
460+ ya = point . ya ,
461+ xpx = xa . c2p ( xval ) ,
462+ ypx = ya . c2p ( yval ) ,
463+ dxy = function ( point ) {
464+ var rad = Math . max ( 3 , point . mrc || 0 ) ,
465+ dx = ( point . x1 + point . x0 ) / 2 - xpx ,
466+ dy = ( point . y1 + point . y0 ) / 2 - ypx ;
467+ return Math . max ( Math . sqrt ( dx * dx + dy * dy ) - rad , 1 - 3 / rad ) ;
468+ } ;
469+ var distance = dxy ( point ) ;
470+ return { distance : distance , index : index } ;
471+ } ) ;
472+ pointsDistances = pointsDistances
473+ . filter ( function ( point ) {
474+ return point . distance <= spikedistance ;
475+ } )
476+ . sort ( function ( a , b ) {
477+ return a . distance - b . distance ;
495478 } ) ;
496- if ( closestPoints . length ) {
497- var closestPt = closestPoints [ 0 ] ;
498- if ( isNumeric ( closestPt . x0 ) && isNumeric ( closestPt . y0 ) ) {
499- var tmpPoint = {
500- xa : closestPt . xa ,
501- ya : closestPt . ya ,
502- x0 : closestPt . x0 ,
503- x1 : closestPt . x1 ,
504- y0 : closestPt . y0 ,
505- y1 : closestPt . y1 ,
506- distance : closestPt . distance ,
507- curveNumber : closestPt . trace . index ,
508- color : closestPt . color ,
509- pointNumber : closestPt . index
510- } ;
511- if ( ! resultPoint || ( resultPoint . distance > tmpPoint . distance ) ) {
512- resultPoint = tmpPoint ;
513- }
514- }
515- }
479+ if ( pointsDistances . length ) {
480+ resultPoint = pointsData [ pointsDistances [ 0 ] . index ] ;
481+ } else {
482+ resultPoint = null ;
516483 }
517- pointData . index = tmpIndex ;
518- pointData . distance = tmpDistance ;
519484 return resultPoint ;
520485 }
521486
487+ function clearClosestPoint ( point ) {
488+ if ( ! point ) return null ;
489+ return {
490+ xa : point . xa ,
491+ ya : point . ya ,
492+ x0 : point . x0 ,
493+ x1 : point . x1 ,
494+ y0 : point . y0 ,
495+ y1 : point . y1 ,
496+ distance : point . distance ,
497+ curveNumber : point . trace . index ,
498+ color : point . color ,
499+ pointNumber : point . index
500+ } ;
501+ }
502+
522503 // if hoverData is empty check for the spikes to draw and quit if there are none
523504 var spikelineOpts = {
524- hovermode : hovermode ,
525505 fullLayout : fullLayout ,
526506 container : fullLayout . _hoverlayer ,
527507 outerContainer : fullLayout . _paperdiv ,
@@ -534,6 +514,22 @@ function _hover(gd, evt, subplot, noHoverEvent) {
534514 } ;
535515 gd . _spikepoints = newspikepoints ;
536516
517+ if ( fullLayout . _has ( 'cartesian' ) ) {
518+ if ( hoverData . length !== 0 ) {
519+ var tmpHPointData = hoverData . filter ( function ( point ) {
520+ return point . ya . showspikes ;
521+ } ) ;
522+ var tmpHPoint = selectClosestPoint ( tmpHPointData , spikedistance ) ;
523+ spikePoints . hLinePoint = clearClosestPoint ( tmpHPoint ) ;
524+
525+ var tmpVPointData = hoverData . filter ( function ( point ) {
526+ return point . xa . showspikes ;
527+ } ) ;
528+ var tmpVPoint = selectClosestPoint ( tmpVPointData , spikedistance ) ;
529+ spikePoints . vLinePoint = clearClosestPoint ( tmpVPoint ) ;
530+ }
531+ }
532+
537533 if ( hoverData . length === 0 ) {
538534 var result = dragElement . unhoverRaw ( gd , evt ) ;
539535 if ( fullLayout . _has ( 'cartesian' ) && ( ( spikePoints . hLinePoint !== null ) || ( spikePoints . vLinePoint !== null ) ) ) {
@@ -580,7 +576,8 @@ function _hover(gd, evt, subplot, noHoverEvent) {
580576 bgColor : bgColor ,
581577 container : fullLayout . _hoverlayer ,
582578 outerContainer : fullLayout . _paperdiv ,
583- commonLabelOpts : fullLayout . hoverlabel
579+ commonLabelOpts : fullLayout . hoverlabel ,
580+ hoverdistance : fullLayout . hoverdistance
584581 } ;
585582
586583 var hoverLabels = createHoverText ( hoverData , labelOpts , gd ) ;
@@ -644,7 +641,7 @@ function createHoverText(hoverData, opts, gd) {
644641 // show the common label, if any, on the axis
645642 // never show a common label in array mode,
646643 // even if sometimes there could be one
647- var showCommonLabel = c0 . distance <= constants . MAXDIST &&
644+ var showCommonLabel = c0 . distance <= opts . hoverdistance &&
648645 ( hovermode === 'x' || hovermode === 'y' ) ;
649646
650647 // all hover traces hoverinfo must contain the hovermode
@@ -1223,7 +1220,6 @@ function cleanPoint(d, hovermode) {
12231220}
12241221
12251222function createSpikelines ( closestPoints , opts ) {
1226- var hovermode = opts . hovermode ;
12271223 var container = opts . container ;
12281224 var fullLayout = opts . fullLayout ;
12291225 var evt = opts . event ;
@@ -1242,12 +1238,13 @@ function createSpikelines(closestPoints, opts) {
12421238
12431239 // Horizontal line (to y-axis)
12441240 if ( showY ) {
1245- var hLinePoint = closestPoints . hLinePoint ;
1246- xa = hLinePoint && hLinePoint . xa ;
1247- ya = hLinePoint && hLinePoint . ya ;
1248- var ySnap = ya . spikesnap ,
1241+ var hLinePoint = closestPoints . hLinePoint ,
12491242 hLinePointX ,
12501243 hLinePointY ;
1244+ xa = hLinePoint && hLinePoint . xa ;
1245+ ya = hLinePoint && hLinePoint . ya ;
1246+ var ySnap = ya . spikesnap ;
1247+
12511248 if ( ySnap === 'cursor' ) {
12521249 hLinePointY = evt . offsetY ;
12531250 } else {
@@ -1256,16 +1253,13 @@ function createSpikelines(closestPoints, opts) {
12561253 hLinePointX = xa . _offset + ( hLinePoint . x0 + hLinePoint . x1 ) / 2 ;
12571254 var dfltHLineColor = tinycolor . readability ( hLinePoint . color , contrastColor ) < 1.5 ?
12581255 Color . contrast ( contrastColor ) : hLinePoint . color ;
1259- var yMode = ya . spikemode ;
1260- if ( hovermode !== 'closest' && yMode . indexOf ( 'toaxis' ) !== - 1 ) {
1261- yMode = yMode . replace ( 'toaxis' , 'across' ) ;
1262- }
1263- var yThickness = ya . spikethickness ;
1264- var yColor = ya . spikecolor || dfltHLineColor ;
1265- var yBB = ya . _boundingBox ;
1266- var xEdge = ( ( yBB . left + yBB . right ) / 2 ) < hLinePointX ? yBB . right : yBB . left ;
1267- var xBase ;
1268- var xEndSpike ;
1256+ var yMode = ya . spikemode ,
1257+ yThickness = ya . spikethickness ,
1258+ yColor = ya . spikecolor || dfltHLineColor ,
1259+ yBB = ya . _boundingBox ,
1260+ xEdge = ( ( yBB . left + yBB . right ) / 2 ) < hLinePointX ? yBB . right : yBB . left ,
1261+ xBase ,
1262+ xEndSpike ;
12691263
12701264 if ( yMode . indexOf ( 'toaxis' ) !== - 1 || yMode . indexOf ( 'across' ) !== - 1 ) {
12711265 if ( yMode . indexOf ( 'toaxis' ) !== - 1 ) {
@@ -1318,31 +1312,29 @@ function createSpikelines(closestPoints, opts) {
13181312 }
13191313
13201314 if ( showX ) {
1321- var vLinePoint = closestPoints . vLinePoint ;
1322- xa = vLinePoint && vLinePoint . xa ;
1323- ya = vLinePoint && vLinePoint . ya ;
1324- var xSnap = xa . spikesnap ,
1315+ var vLinePoint = closestPoints . vLinePoint ,
13251316 vLinePointX ,
13261317 vLinePointY ;
1318+
1319+ xa = vLinePoint && vLinePoint . xa ;
1320+ ya = vLinePoint && vLinePoint . ya ;
1321+ var xSnap = xa . spikesnap ;
1322+
13271323 if ( xSnap === 'cursor' ) {
13281324 vLinePointX = evt . offsetX ;
13291325 } else {
13301326 vLinePointX = xa . _offset + ( vLinePoint . x0 + vLinePoint . x1 ) / 2 ;
13311327 }
13321328 vLinePointY = ya . _offset + ( vLinePoint . y0 + vLinePoint . y1 ) / 2 ;
13331329 var dfltVLineColor = tinycolor . readability ( vLinePoint . color , contrastColor ) < 1.5 ?
1334- Color . contrast ( contrastColor ) : vLinePoint . color ;
1335- var xMode = xa . spikemode ;
1336- if ( hovermode !== 'closest' && xMode . indexOf ( 'toaxis' ) !== - 1 ) {
1337- xMode = xMode . replace ( 'toaxis' , 'across' ) ;
1338- }
1339- var xThickness = xa . spikethickness ;
1340- var xColor = xa . spikecolor || dfltVLineColor ;
1341- var xBB = xa . _boundingBox ;
1342- var yEdge = ( ( xBB . top + xBB . bottom ) / 2 ) < vLinePointY ? xBB . bottom : xBB . top ;
1343-
1344- var yBase ;
1345- var yEndSpike ;
1330+ Color . contrast ( contrastColor ) : vLinePoint . color ;
1331+ var xMode = xa . spikemode ,
1332+ xThickness = xa . spikethickness ,
1333+ xColor = xa . spikecolor || dfltVLineColor ,
1334+ xBB = xa . _boundingBox ,
1335+ yEdge = ( ( xBB . top + xBB . bottom ) / 2 ) < vLinePointY ? xBB . bottom : xBB . top ,
1336+ yBase ,
1337+ yEndSpike ;
13461338
13471339 if ( xMode . indexOf ( 'toaxis' ) !== - 1 || xMode . indexOf ( 'across' ) !== - 1 ) {
13481340 if ( xMode . indexOf ( 'toaxis' ) !== - 1 ) {
0 commit comments