@@ -267,7 +267,7 @@ function handleCartesian(gd, ev) {
267267modeBarButtons . zoom3d = {
268268 name : 'zoom3d' ,
269269 title : 'Zoom' ,
270- attr : 'dragmode' ,
270+ attr : 'scene. dragmode' ,
271271 val : 'zoom' ,
272272 icon : Icons . zoombox ,
273273 click : handleDrag3d
@@ -276,7 +276,7 @@ modeBarButtons.zoom3d = {
276276modeBarButtons . pan3d = {
277277 name : 'pan3d' ,
278278 title : 'Pan' ,
279- attr : 'dragmode' ,
279+ attr : 'scene. dragmode' ,
280280 val : 'pan' ,
281281 icon : Icons . pan ,
282282 click : handleDrag3d
@@ -285,7 +285,7 @@ modeBarButtons.pan3d = {
285285modeBarButtons . orbitRotation = {
286286 name : 'orbitRotation' ,
287287 title : 'orbital rotation' ,
288- attr : 'dragmode' ,
288+ attr : 'scene. dragmode' ,
289289 val : 'orbit' ,
290290 icon : Icons [ '3d_rotate' ] ,
291291 click : handleDrag3d
@@ -294,7 +294,7 @@ modeBarButtons.orbitRotation = {
294294modeBarButtons . tableRotation = {
295295 name : 'tableRotation' ,
296296 title : 'turntable rotation' ,
297- attr : 'dragmode' ,
297+ attr : 'scene. dragmode' ,
298298 val : 'turntable' ,
299299 icon : Icons [ 'z-axis' ] ,
300300 click : handleDrag3d
@@ -304,14 +304,16 @@ function handleDrag3d(gd, ev) {
304304 var button = ev . currentTarget ,
305305 attr = button . getAttribute ( 'data-attr' ) ,
306306 val = button . getAttribute ( 'data-val' ) || true ,
307+ fullLayout = gd . _fullLayout ,
308+ sceneIds = Plotly . Plots . getSubplotIds ( fullLayout , 'gl3d' ) ,
307309 layoutUpdate = { } ;
308310
309- layoutUpdate [ attr ] = val ;
311+ var parts = attr . split ( '.' ) ;
312+
313+ for ( var i = 0 ; i < sceneIds . length ; i ++ ) {
314+ layoutUpdate [ sceneIds [ i ] + '.' + parts [ 1 ] ] = val ;
315+ }
310316
311- /*
312- * Dragmode will go through the relayout -> doplot -> scene.plot()
313- * routine where the dragmode will be set in scene.plot()
314- */
315317 Plotly . relayout ( gd , layoutUpdate ) ;
316318}
317319
@@ -334,29 +336,19 @@ modeBarButtons.resetCameraLastSave3d = {
334336function handleCamera3d ( gd , ev ) {
335337 var button = ev . currentTarget ,
336338 attr = button . getAttribute ( 'data-attr' ) ,
337- layout = gd . layout ,
338339 fullLayout = gd . _fullLayout ,
339340 sceneIds = Plotly . Plots . getSubplotIds ( fullLayout , 'gl3d' ) ;
340341
341342 for ( var i = 0 ; i < sceneIds . length ; i ++ ) {
342343 var sceneId = sceneIds [ i ] ,
343- sceneLayout = layout [ sceneId ] ,
344344 fullSceneLayout = fullLayout [ sceneId ] ,
345345 scene = fullSceneLayout . _scene ;
346346
347- if ( ! sceneLayout || attr === 'resetDefault' ) scene . setCameraToDefault ( ) ;
347+ if ( attr === 'resetDefault' ) scene . setCameraToDefault ( ) ;
348348 else if ( attr === 'resetLastSave' ) {
349-
350- var cameraPos = sceneLayout . camera ;
351- if ( cameraPos ) scene . setCamera ( cameraPos ) ;
352- else scene . setCameraToDefault ( ) ;
349+ scene . setCamera ( fullSceneLayout . camera ) ;
353350 }
354351 }
355-
356- /*
357- * TODO have a sceneLastTouched in _fullLayout to only
358- * update the camera of the scene last touched by the user
359- */
360352}
361353
362354modeBarButtons . hoverClosest3d = {
@@ -367,50 +359,58 @@ modeBarButtons.hoverClosest3d = {
367359 toggle : true ,
368360 icon : Icons . tooltip_basic ,
369361 gravity : 'ne' ,
370- click : function ( gd , ev ) {
371- var button = ev . currentTarget ,
372- val = JSON . parse ( button . getAttribute ( 'data-val' ) ) || false ,
373- fullLayout = gd . _fullLayout ,
374- sceneIds = Plotly . Plots . getSubplotIds ( fullLayout , 'gl3d' ) ;
375-
376- var axes = [ 'xaxis' , 'yaxis' , 'zaxis' ] ,
377- spikeAttrs = [ 'showspikes' , 'spikesides' , 'spikethickness' , 'spikecolor' ] ;
378-
379- // initialize 'current spike' object to be stored in the DOM
380- var currentSpikes = { } ,
381- axisSpikes = { } ,
382- layoutUpdate = { } ;
383-
384- if ( val ) {
385- layoutUpdate = val ;
386- button . setAttribute ( 'data-val' , JSON . stringify ( null ) ) ;
387- }
388- else {
389- layoutUpdate = { 'allaxes.showspikes' : false } ;
390-
391- for ( var i = 0 ; i < sceneIds . length ; i ++ ) {
392- var sceneId = sceneIds [ i ] ,
393- sceneLayout = fullLayout [ sceneId ] ,
394- sceneSpikes = currentSpikes [ sceneId ] = { } ;
395-
396- // copy all the current spike attrs
397- for ( var j = 0 ; j < 3 ; j ++ ) {
398- var axis = axes [ j ] ;
399- axisSpikes = sceneSpikes [ axis ] = { } ;
400-
401- for ( var k = 0 ; k < spikeAttrs . length ; k ++ ) {
402- var spikeAttr = spikeAttrs [ k ] ;
403- axisSpikes [ spikeAttr ] = sceneLayout [ axis ] [ spikeAttr ] ;
404- }
362+ click : handleHover3d
363+ } ;
364+
365+ function handleHover3d ( gd , ev ) {
366+ var button = ev . currentTarget ,
367+ val = button . _previousVal || false ,
368+ layout = gd . layout ,
369+ fullLayout = gd . _fullLayout ,
370+ sceneIds = Plotly . Plots . getSubplotIds ( fullLayout , 'gl3d' ) ;
371+
372+ var axes = [ 'xaxis' , 'yaxis' , 'zaxis' ] ,
373+ spikeAttrs = [ 'showspikes' , 'spikesides' , 'spikethickness' , 'spikecolor' ] ;
374+
375+ // initialize 'current spike' object to be stored in the DOM
376+ var currentSpikes = { } ,
377+ axisSpikes = { } ,
378+ layoutUpdate = { } ;
379+
380+ if ( val ) {
381+ layoutUpdate = Lib . extendDeep ( layout , val ) ;
382+ button . _previousVal = null ;
383+ }
384+ else {
385+ layoutUpdate = {
386+ 'allaxes.showspikes' : false
387+ } ;
388+
389+ for ( var i = 0 ; i < sceneIds . length ; i ++ ) {
390+ var sceneId = sceneIds [ i ] ,
391+ sceneLayout = fullLayout [ sceneId ] ,
392+ sceneSpikes = currentSpikes [ sceneId ] = { } ;
393+
394+ sceneSpikes . hovermode = sceneLayout . hovermode ;
395+ layoutUpdate [ sceneId + '.hovermode' ] = false ;
396+
397+ // copy all the current spike attrs
398+ for ( var j = 0 ; j < 3 ; j ++ ) {
399+ var axis = axes [ j ] ;
400+ axisSpikes = sceneSpikes [ axis ] = { } ;
401+
402+ for ( var k = 0 ; k < spikeAttrs . length ; k ++ ) {
403+ var spikeAttr = spikeAttrs [ k ] ;
404+ axisSpikes [ spikeAttr ] = sceneLayout [ axis ] [ spikeAttr ] ;
405405 }
406406 }
407-
408- button . setAttribute ( 'data-val' , JSON . stringify ( currentSpikes ) ) ;
409407 }
410408
411- Plotly . relayout ( gd , layoutUpdate ) ;
409+ button . _previousVal = Lib . extendDeep ( { } , currentSpikes ) ;
412410 }
413- } ;
411+
412+ Plotly . relayout ( gd , layoutUpdate ) ;
413+ }
414414
415415modeBarButtons . zoomInGeo = {
416416 name : 'zoomInGeo' ,
@@ -447,7 +447,7 @@ modeBarButtons.hoverClosestGeo = {
447447 toggle : true ,
448448 icon : Icons . tooltip_basic ,
449449 gravity : 'ne' ,
450- click : handleGeo
450+ click : toggleHover
451451} ;
452452
453453function handleGeo ( gd , ev ) {
@@ -468,7 +468,6 @@ function handleGeo(gd, ev) {
468468 geo . render ( ) ;
469469 }
470470 else if ( attr === 'reset' ) geo . zoomReset ( ) ;
471- else if ( attr === 'hovermode' ) geo . showHover = ! geo . showHover ;
472471 }
473472}
474473
@@ -494,7 +493,54 @@ modeBarButtons.hoverClosestPie = {
494493} ;
495494
496495function toggleHover ( gd ) {
497- var newHover = gd . _fullLayout . hovermode ? false : 'closest' ;
496+ var fullLayout = gd . _fullLayout ;
497+
498+ var onHoverVal ;
499+ if ( fullLayout . _hasCartesian ) {
500+ onHoverVal = fullLayout . _isHoriz ? 'y' : 'x' ;
501+ }
502+ else onHoverVal = 'closest' ;
503+
504+ var newHover = gd . _fullLayout . hovermode ? false : onHoverVal ;
498505
499506 Plotly . relayout ( gd , 'hovermode' , newHover ) ;
500507}
508+
509+ // buttons when more then one plot types are present
510+
511+ modeBarButtons . toggleHover = {
512+ name : 'toggleHover' ,
513+ title : 'Toggle show closest data on hover' ,
514+ attr : 'hovermode' ,
515+ val : null ,
516+ toggle : true ,
517+ icon : Icons . tooltip_basic ,
518+ gravity : 'ne' ,
519+ click : function ( gd , ev ) {
520+ toggleHover ( gd ) ;
521+
522+ // the 3d hovermode update must come
523+ // last so that layout.hovermode update does not
524+ // override scene?.hovermode?.layout.
525+ handleHover3d ( gd , ev ) ;
526+ }
527+ } ;
528+
529+ modeBarButtons . resetViews = {
530+ name : 'resetViews' ,
531+ title : 'Reset views' ,
532+ icon : Icons . home ,
533+ click : function ( gd , ev ) {
534+ var button = ev . currentTarget ;
535+
536+ button . setAttribute ( 'data-attr' , 'zoom' ) ;
537+ button . setAttribute ( 'data-val' , 'reset' ) ;
538+ handleCartesian ( gd , ev ) ;
539+
540+ button . setAttribute ( 'data-attr' , 'resetLastSave' ) ;
541+ handleCamera3d ( gd , ev ) ;
542+
543+ // N.B handleCamera3d also triggers a replot for
544+ // geo subplots.
545+ }
546+ } ;
0 commit comments