99
1010'use strict' ;
1111
12- var createCamera = require ( 'gl-plot3d' ) . createCamera ;
13- var createPlot = require ( 'gl-plot3d' ) . createScene ;
12+ var glPlot3d = require ( 'gl-plot3d' ) ;
13+ var createCamera = glPlot3d . createCamera ;
14+ var createPlot = glPlot3d . createScene ;
15+
1416var getContext = require ( 'webgl-context' ) ;
1517var passiveSupported = require ( 'has-passive-events' ) ;
1618
@@ -246,10 +248,10 @@ function tryCreatePlot(scene, cameraObject, pixelRatio, canvas, gl) {
246248 return failed < 2 ;
247249}
248250
249- function initializeGLPlot ( scene , pixelRatio , canvas , gl ) {
251+ function initializeGLPlot ( scene , canvas , gl ) {
250252 scene . initializeGLCamera ( ) ;
251253
252- var success = tryCreatePlot ( scene , scene . camera , pixelRatio , canvas , gl ) ;
254+ var success = tryCreatePlot ( scene , scene . camera , scene . pixelRatio , canvas , gl ) ;
253255 /*
254256 * createPlot will throw when webgl is not enabled in the client.
255257 * Lets return an instance of the module with all functions noop'd.
@@ -259,22 +261,48 @@ function initializeGLPlot(scene, pixelRatio, canvas, gl) {
259261 if ( ! success ) return showNoWebGlMsg ( scene ) ;
260262
261263 var gd = scene . graphDiv ;
264+ var layout = gd . layout ;
265+
266+ var makeUpdate = function ( ) {
267+ var update = { } ;
268+
269+ if ( scene . isCameraChanged ( layout ) ) {
270+ // camera updates
271+ update [ scene . id + '.camera' ] = scene . getCamera ( ) ;
272+ }
273+
274+ if ( scene . isAspectChanged ( layout ) ) {
275+ // scene updates
276+ update [ scene . id + '.aspectratio' ] = scene . glplot . getAspectratio ( ) ;
277+ }
278+
279+ return update ;
280+ } ;
262281
263282 var relayoutCallback = function ( scene ) {
264283 if ( scene . fullSceneLayout . dragmode === false ) return ;
265284
266- var update = { } ;
267- update [ scene . id + '.camera' ] = getLayoutCamera ( scene . camera ) ;
268- scene . saveCamera ( gd . layout ) ;
285+ var update = makeUpdate ( ) ;
286+ scene . saveLayout ( layout ) ;
269287 scene . graphDiv . emit ( 'plotly_relayout' , update ) ;
270288 } ;
271289
272290 scene . glplot . canvas . addEventListener ( 'mouseup' , function ( ) {
273291 relayoutCallback ( scene ) ;
274292 } ) ;
275293
276- scene . glplot . canvas . addEventListener ( 'wheel' , function ( ) {
294+ scene . glplot . canvas . addEventListener ( 'wheel' , function ( e ) {
277295 if ( gd . _context . _scrollZoom . gl3d ) {
296+ if ( scene . glplot . camera . _ortho ) {
297+ var s = ( e . deltaX > e . deltaY ) ? 1.1 : 1.0 / 1.1 ;
298+ var o = scene . glplot . getAspectratio ( ) ;
299+ scene . glplot . setAspectratio ( {
300+ x : s * o . x ,
301+ y : s * o . y ,
302+ z : s * o . z
303+ } ) ;
304+ }
305+
278306 relayoutCallback ( scene ) ;
279307 }
280308 } , passiveSupported ? { passive : false } : false ) ;
@@ -283,8 +311,7 @@ function initializeGLPlot(scene, pixelRatio, canvas, gl) {
283311 if ( scene . fullSceneLayout . dragmode === false ) return ;
284312 if ( scene . camera . mouseListener . buttons === 0 ) return ;
285313
286- var update = { } ;
287- update [ scene . id + '.camera' ] = getLayoutCamera ( scene . camera ) ;
314+ var update = makeUpdate ( ) ;
288315 scene . graphDiv . emit ( 'plotly_relayouting' , update ) ;
289316 } ) ;
290317
@@ -366,7 +393,7 @@ function Scene(options, fullLayout) {
366393 this . convertAnnotations = Registry . getComponentMethod ( 'annotations3d' , 'convert' ) ;
367394 this . drawAnnotations = Registry . getComponentMethod ( 'annotations3d' , 'draw' ) ;
368395
369- initializeGLPlot ( this , this . pixelRatio ) ;
396+ initializeGLPlot ( this ) ;
370397}
371398
372399var proto = Scene . prototype ;
@@ -390,16 +417,15 @@ proto.recoverContext = function() {
390417 var scene = this ;
391418 var gl = this . glplot . gl ;
392419 var canvas = this . glplot . canvas ;
393- var camera = this . glplot . camera ;
394- var pixelRatio = this . glplot . pixelRatio ;
420+
395421 this . glplot . dispose ( ) ;
396422
397423 function tryRecover ( ) {
398424 if ( gl . isContextLost ( ) ) {
399425 requestAnimationFrame ( tryRecover ) ;
400426 return ;
401427 }
402- if ( ! initializeGLPlot ( scene , camera , pixelRatio , canvas , gl ) ) {
428+ if ( ! initializeGLPlot ( scene , canvas , gl ) ) {
403429 Lib . error ( 'Catastrophic and unrecoverable WebGL error. Context lost.' ) ;
404430 return ;
405431 }
@@ -496,7 +522,7 @@ proto.plot = function(sceneData, fullLayout, layout) {
496522 this . spikeOptions . merge ( fullSceneLayout ) ;
497523
498524 // Update camera and camera mode
499- this . setCamera ( fullSceneLayout . camera ) ;
525+ this . setViewport ( fullSceneLayout ) ;
500526 this . updateFx ( fullSceneLayout . dragmode , fullSceneLayout . hovermode ) ;
501527 this . camera . enableWheel = this . graphDiv . _context . _scrollZoom . gl3d ;
502528
@@ -720,8 +746,16 @@ proto.plot = function(sceneData, fullLayout, layout) {
720746 * Finally assign the computed aspecratio to the glplot module. This will have an effect
721747 * on the next render cycle.
722748 */
723- this . glplot . aspect = aspectRatio ;
724-
749+ this . glplot . setAspectratio ( fullSceneLayout . aspectratio ) ;
750+
751+ // save 'initial' camera view settings for modebar button
752+ if ( ! this . viewInitial . aspectratio ) {
753+ this . viewInitial . aspectratio = {
754+ x : fullSceneLayout . aspectratio . x ,
755+ y : fullSceneLayout . aspectratio . y ,
756+ z : fullSceneLayout . aspectratio . z
757+ } ;
758+ }
725759
726760 // Update frame position for multi plots
727761 var domain = fullSceneLayout . domain || null ;
@@ -751,18 +785,18 @@ proto.destroy = function() {
751785 this . glplot = null ;
752786} ;
753787
754- // getOrbitCamera :: plotly_coords -> orbit_camera_coords
788+ // getCameraArrays :: plotly_coords -> gl-plot3d_coords
755789// inverse of getLayoutCamera
756- function getOrbitCamera ( camera ) {
790+ function getCameraArrays ( camera ) {
757791 return [
758792 [ camera . eye . x , camera . eye . y , camera . eye . z ] ,
759793 [ camera . center . x , camera . center . y , camera . center . z ] ,
760794 [ camera . up . x , camera . up . y , camera . up . z ]
761795 ] ;
762796}
763797
764- // getLayoutCamera :: orbit_camera_coords -> plotly_coords
765- // inverse of getOrbitCamera
798+ // getLayoutCamera :: gl-plot3d_coords -> plotly_coords
799+ // inverse of getCameraArrays
766800function getLayoutCamera ( camera ) {
767801 return {
768802 up : { x : camera . up [ 0 ] , y : camera . up [ 1 ] , z : camera . up [ 2 ] } ,
@@ -772,24 +806,25 @@ function getLayoutCamera(camera) {
772806 } ;
773807}
774808
775- // get camera position in plotly coords from 'orbit-camera ' coords
776- proto . getCamera = function getCamera ( ) {
809+ // get camera position in plotly coords from 'gl-plot3d ' coords
810+ proto . getCamera = function ( ) {
777811 this . glplot . camera . view . recalcMatrix ( this . camera . view . lastT ( ) ) ;
778812 return getLayoutCamera ( this . glplot . camera ) ;
779813} ;
780814
781- // set camera position with a set of plotly coords
782- proto . setCamera = function setCamera ( cameraData ) {
783- this . glplot . camera . lookAt . apply ( this , getOrbitCamera ( cameraData ) ) ;
815+ // set gl-plot3d camera position and scene aspects with a set of plotly coords
816+ proto . setViewport = function ( sceneLayout ) {
817+ var cameraData = sceneLayout . camera ;
818+
819+ this . glplot . camera . lookAt . apply ( this , getCameraArrays ( cameraData ) ) ;
820+ this . glplot . setAspectratio ( sceneLayout . aspectratio ) ;
784821
785822 var newOrtho = ( cameraData . projection . type === 'orthographic' ) ;
786823 var oldOrtho = this . glplot . camera . _ortho ;
787824
788825 if ( newOrtho !== oldOrtho ) {
789826 this . glplot . redraw ( ) ;
790827
791- var pixelRatio = this . glplot . pixelRatio ;
792-
793828 var RGBA = this . glplot . clearColor ;
794829 this . glplot . gl . clearColor (
795830 RGBA [ 0 ] , RGBA [ 1 ] , RGBA [ 2 ] , RGBA [ 3 ]
@@ -801,32 +836,30 @@ proto.setCamera = function setCamera(cameraData) {
801836
802837 this . glplot . dispose ( ) ;
803838
804- initializeGLPlot ( this , pixelRatio ) ;
839+ initializeGLPlot ( this ) ;
805840 this . glplot . camera . _ortho = newOrtho ;
806841 }
807842} ;
808843
809- // save camera to user layout (i.e. gd.layout)
810- proto . saveCamera = function saveCamera ( layout ) {
811- var fullLayout = this . fullLayout ;
844+ proto . isCameraChanged = function ( layout ) {
812845 var cameraData = this . getCamera ( ) ;
813846 var cameraNestedProp = Lib . nestedProperty ( layout , this . id + '.camera' ) ;
814847 var cameraDataLastSave = cameraNestedProp . get ( ) ;
815- var hasChanged = false ;
816848
817849 function same ( x , y , i , j ) {
818850 var vectors = [ 'up' , 'center' , 'eye' ] ;
819851 var components = [ 'x' , 'y' , 'z' ] ;
820852 return y [ vectors [ i ] ] && ( x [ vectors [ i ] ] [ components [ j ] ] === y [ vectors [ i ] ] [ components [ j ] ] ) ;
821853 }
822854
855+ var changed = false ;
823856 if ( cameraDataLastSave === undefined ) {
824- hasChanged = true ;
857+ changed = true ;
825858 } else {
826859 for ( var i = 0 ; i < 3 ; i ++ ) {
827860 for ( var j = 0 ; j < 3 ; j ++ ) {
828861 if ( ! same ( cameraData , cameraDataLastSave , i , j ) ) {
829- hasChanged = true ;
862+ changed = true ;
830863 break ;
831864 }
832865 }
@@ -835,19 +868,75 @@ proto.saveCamera = function saveCamera(layout) {
835868 if ( ! cameraDataLastSave . projection || (
836869 cameraData . projection &&
837870 cameraData . projection . type !== cameraDataLastSave . projection . type ) ) {
838- hasChanged = true ;
871+ changed = true ;
839872 }
840873 }
841874
875+ return changed ;
876+ } ;
877+
878+ proto . isAspectChanged = function ( layout ) {
879+ var aspectData = this . glplot . getAspectratio ( ) ;
880+ var aspectNestedProp = Lib . nestedProperty ( layout , this . id + '.aspectratio' ) ;
881+ var aspectDataLastSave = aspectNestedProp . get ( ) ;
882+
883+ return (
884+ aspectDataLastSave === undefined || (
885+ aspectDataLastSave . x !== aspectData . x ||
886+ aspectDataLastSave . y !== aspectData . y ||
887+ aspectDataLastSave . z !== aspectData . z
888+ ) ) ;
889+ } ;
890+
891+ // save camera to user layout (i.e. gd.layout)
892+ proto . saveLayout = function ( layout ) {
893+ var fullLayout = this . fullLayout ;
894+
895+ var cameraData ;
896+ var cameraNestedProp ;
897+ var cameraDataLastSave ;
898+
899+ var aspectData ;
900+ var aspectNestedProp ;
901+ var aspectDataLastSave ;
902+
903+ var cameraChanged = this . isCameraChanged ( layout ) ;
904+ var aspectChanged = this . isAspectChanged ( layout ) ;
905+
906+ var hasChanged = cameraChanged || aspectChanged ;
842907 if ( hasChanged ) {
843908 var preGUI = { } ;
844- preGUI [ this . id + '.camera' ] = cameraDataLastSave ;
909+ if ( cameraChanged ) {
910+ cameraData = this . getCamera ( ) ;
911+ cameraNestedProp = Lib . nestedProperty ( layout , this . id + '.camera' ) ;
912+ cameraDataLastSave = cameraNestedProp . get ( ) ;
913+
914+ preGUI [ this . id + '.camera' ] = cameraDataLastSave ;
915+ }
916+ if ( aspectChanged ) {
917+ aspectData = this . glplot . getAspectratio ( ) ;
918+ aspectNestedProp = Lib . nestedProperty ( layout , this . id + '.aspectratio' ) ;
919+ aspectDataLastSave = aspectNestedProp . get ( ) ;
920+
921+ preGUI [ this . id + '.aspectratio' ] = aspectDataLastSave ;
922+ }
845923 Registry . call ( '_storeDirectGUIEdit' , layout , fullLayout . _preGUI , preGUI ) ;
846924
847- cameraNestedProp . set ( cameraData ) ;
925+ if ( cameraChanged ) {
926+ cameraNestedProp . set ( cameraData ) ;
848927
849- var cameraFullNP = Lib . nestedProperty ( fullLayout , this . id + '.camera' ) ;
850- cameraFullNP . set ( cameraData ) ;
928+ var cameraFullNP = Lib . nestedProperty ( fullLayout , this . id + '.camera' ) ;
929+ cameraFullNP . set ( cameraData ) ;
930+ }
931+
932+ if ( aspectChanged ) {
933+ aspectNestedProp . set ( aspectData ) ;
934+
935+ var aspectFullNP = Lib . nestedProperty ( fullLayout , this . id + '.aspectratio' ) ;
936+ aspectFullNP . set ( aspectData ) ;
937+
938+ this . glplot . redraw ( ) ;
939+ }
851940 }
852941
853942 return hasChanged ;
0 commit comments