@@ -24,7 +24,6 @@ var Registry = require('../../registry');
24
24
25
25
var helpers = require ( './helpers' ) ;
26
26
var constants = require ( './constants' ) ;
27
- var getTraceColor = require ( '../../traces/scatter/get_trace_color' ) ;
28
27
29
28
// hover labels for multiple horizontal bars get tilted by some angle,
30
29
// then need to be offset differently if they overlap
@@ -145,7 +144,8 @@ exports.loneHover = function loneHover(hoverItem, opts) {
145
144
rotateLabels : false ,
146
145
bgColor : opts . bgColor || Color . background ,
147
146
container : container3 ,
148
- outerContainer : outerContainer3
147
+ outerContainer : outerContainer3 ,
148
+ hoverdistance : constants . MAXDIST
149
149
} ;
150
150
151
151
var hoverLabel = createHoverText ( [ pointData ] , fullOpts , opts . gd ) ;
@@ -208,6 +208,9 @@ function _hover(gd, evt, subplot, noHoverEvent) {
208
208
return dragElement . unhoverRaw ( gd , evt ) ;
209
209
}
210
210
211
+ var hoverdistance = fullLayout . hoverdistance === 0 ? Infinity : fullLayout . hoverdistance ;
212
+ var spikedistance = fullLayout . spikedistance === 0 ? Infinity : fullLayout . spikedistance ;
213
+
211
214
// hoverData: the set of candidate points we've found to highlight
212
215
var hoverData = [ ] ,
213
216
@@ -332,9 +335,6 @@ function _hover(gd, evt, subplot, noHoverEvent) {
332
335
// within one trace mode can sometimes be overridden
333
336
mode = hovermode ;
334
337
335
- var hoverdistance = fullLayout . hoverdistance === 0 ? Infinity : fullLayout . hoverdistance ;
336
- var spikedistance = fullLayout . spikedistance === 0 ? Infinity : fullLayout . spikedistance ;
337
-
338
338
// container for new point, also used to pass info into module.hoverPoints
339
339
pointData = {
340
340
// trace properties
@@ -389,17 +389,6 @@ function _hover(gd, evt, subplot, noHoverEvent) {
389
389
yval = yvalArray [ subploti ] ;
390
390
}
391
391
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
-
403
392
// Now find the points.
404
393
if ( trace . _module && trace . _module . hoverPoints ) {
405
394
var newPoints = trace . _module . hoverPoints ( pointData , xval , yval , mode , fullLayout . _hoverlayer ) ;
@@ -423,105 +412,96 @@ function _hover(gd, evt, subplot, noHoverEvent) {
423
412
hoverData . splice ( 0 , closedataPreviousLength ) ;
424
413
distance = hoverData [ 0 ] . distance ;
425
414
}
426
- }
427
415
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
+ }
477
451
}
478
452
}
479
453
}
480
454
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 ;
495
478
} ) ;
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 ;
516
483
}
517
- pointData . index = tmpIndex ;
518
- pointData . distance = tmpDistance ;
519
484
return resultPoint ;
520
485
}
521
486
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
+
522
503
// if hoverData is empty check for the spikes to draw and quit if there are none
523
504
var spikelineOpts = {
524
- hovermode : hovermode ,
525
505
fullLayout : fullLayout ,
526
506
container : fullLayout . _hoverlayer ,
527
507
outerContainer : fullLayout . _paperdiv ,
@@ -534,6 +514,22 @@ function _hover(gd, evt, subplot, noHoverEvent) {
534
514
} ;
535
515
gd . _spikepoints = newspikepoints ;
536
516
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
+
537
533
if ( hoverData . length === 0 ) {
538
534
var result = dragElement . unhoverRaw ( gd , evt ) ;
539
535
if ( fullLayout . _has ( 'cartesian' ) && ( ( spikePoints . hLinePoint !== null ) || ( spikePoints . vLinePoint !== null ) ) ) {
@@ -580,7 +576,8 @@ function _hover(gd, evt, subplot, noHoverEvent) {
580
576
bgColor : bgColor ,
581
577
container : fullLayout . _hoverlayer ,
582
578
outerContainer : fullLayout . _paperdiv ,
583
- commonLabelOpts : fullLayout . hoverlabel
579
+ commonLabelOpts : fullLayout . hoverlabel ,
580
+ hoverdistance : fullLayout . hoverdistance
584
581
} ;
585
582
586
583
var hoverLabels = createHoverText ( hoverData , labelOpts , gd ) ;
@@ -644,7 +641,7 @@ function createHoverText(hoverData, opts, gd) {
644
641
// show the common label, if any, on the axis
645
642
// never show a common label in array mode,
646
643
// even if sometimes there could be one
647
- var showCommonLabel = c0 . distance <= constants . MAXDIST &&
644
+ var showCommonLabel = c0 . distance <= opts . hoverdistance &&
648
645
( hovermode === 'x' || hovermode === 'y' ) ;
649
646
650
647
// all hover traces hoverinfo must contain the hovermode
@@ -1223,7 +1220,6 @@ function cleanPoint(d, hovermode) {
1223
1220
}
1224
1221
1225
1222
function createSpikelines ( closestPoints , opts ) {
1226
- var hovermode = opts . hovermode ;
1227
1223
var container = opts . container ;
1228
1224
var fullLayout = opts . fullLayout ;
1229
1225
var evt = opts . event ;
@@ -1242,12 +1238,13 @@ function createSpikelines(closestPoints, opts) {
1242
1238
1243
1239
// Horizontal line (to y-axis)
1244
1240
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 ,
1249
1242
hLinePointX ,
1250
1243
hLinePointY ;
1244
+ xa = hLinePoint && hLinePoint . xa ;
1245
+ ya = hLinePoint && hLinePoint . ya ;
1246
+ var ySnap = ya . spikesnap ;
1247
+
1251
1248
if ( ySnap === 'cursor' ) {
1252
1249
hLinePointY = evt . offsetY ;
1253
1250
} else {
@@ -1256,16 +1253,13 @@ function createSpikelines(closestPoints, opts) {
1256
1253
hLinePointX = xa . _offset + ( hLinePoint . x0 + hLinePoint . x1 ) / 2 ;
1257
1254
var dfltHLineColor = tinycolor . readability ( hLinePoint . color , contrastColor ) < 1.5 ?
1258
1255
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 ;
1269
1263
1270
1264
if ( yMode . indexOf ( 'toaxis' ) !== - 1 || yMode . indexOf ( 'across' ) !== - 1 ) {
1271
1265
if ( yMode . indexOf ( 'toaxis' ) !== - 1 ) {
@@ -1318,31 +1312,29 @@ function createSpikelines(closestPoints, opts) {
1318
1312
}
1319
1313
1320
1314
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 ,
1325
1316
vLinePointX ,
1326
1317
vLinePointY ;
1318
+
1319
+ xa = vLinePoint && vLinePoint . xa ;
1320
+ ya = vLinePoint && vLinePoint . ya ;
1321
+ var xSnap = xa . spikesnap ;
1322
+
1327
1323
if ( xSnap === 'cursor' ) {
1328
1324
vLinePointX = evt . offsetX ;
1329
1325
} else {
1330
1326
vLinePointX = xa . _offset + ( vLinePoint . x0 + vLinePoint . x1 ) / 2 ;
1331
1327
}
1332
1328
vLinePointY = ya . _offset + ( vLinePoint . y0 + vLinePoint . y1 ) / 2 ;
1333
1329
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 ;
1346
1338
1347
1339
if ( xMode . indexOf ( 'toaxis' ) !== - 1 || xMode . indexOf ( 'across' ) !== - 1 ) {
1348
1340
if ( xMode . indexOf ( 'toaxis' ) !== - 1 ) {
0 commit comments