9
9
10
10
'use strict' ;
11
11
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
+
14
16
var getContext = require ( 'webgl-context' ) ;
15
17
var passiveSupported = require ( 'has-passive-events' ) ;
16
18
@@ -246,10 +248,10 @@ function tryCreatePlot(scene, cameraObject, pixelRatio, canvas, gl) {
246
248
return failed < 2 ;
247
249
}
248
250
249
- function initializeGLPlot ( scene , pixelRatio , canvas , gl ) {
251
+ function initializeGLPlot ( scene , canvas , gl ) {
250
252
scene . initializeGLCamera ( ) ;
251
253
252
- var success = tryCreatePlot ( scene , scene . camera , pixelRatio , canvas , gl ) ;
254
+ var success = tryCreatePlot ( scene , scene . camera , scene . pixelRatio , canvas , gl ) ;
253
255
/*
254
256
* createPlot will throw when webgl is not enabled in the client.
255
257
* Lets return an instance of the module with all functions noop'd.
@@ -259,22 +261,48 @@ function initializeGLPlot(scene, pixelRatio, canvas, gl) {
259
261
if ( ! success ) return showNoWebGlMsg ( scene ) ;
260
262
261
263
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
+ } ;
262
281
263
282
var relayoutCallback = function ( scene ) {
264
283
if ( scene . fullSceneLayout . dragmode === false ) return ;
265
284
266
- var update = { } ;
267
- update [ scene . id + '.camera' ] = getLayoutCamera ( scene . camera ) ;
268
- scene . saveCamera ( gd . layout ) ;
285
+ var update = makeUpdate ( ) ;
286
+ scene . saveLayout ( layout ) ;
269
287
scene . graphDiv . emit ( 'plotly_relayout' , update ) ;
270
288
} ;
271
289
272
290
scene . glplot . canvas . addEventListener ( 'mouseup' , function ( ) {
273
291
relayoutCallback ( scene ) ;
274
292
} ) ;
275
293
276
- scene . glplot . canvas . addEventListener ( 'wheel' , function ( ) {
294
+ scene . glplot . canvas . addEventListener ( 'wheel' , function ( e ) {
277
295
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
+
278
306
relayoutCallback ( scene ) ;
279
307
}
280
308
} , passiveSupported ? { passive : false } : false ) ;
@@ -283,8 +311,7 @@ function initializeGLPlot(scene, pixelRatio, canvas, gl) {
283
311
if ( scene . fullSceneLayout . dragmode === false ) return ;
284
312
if ( scene . camera . mouseListener . buttons === 0 ) return ;
285
313
286
- var update = { } ;
287
- update [ scene . id + '.camera' ] = getLayoutCamera ( scene . camera ) ;
314
+ var update = makeUpdate ( ) ;
288
315
scene . graphDiv . emit ( 'plotly_relayouting' , update ) ;
289
316
} ) ;
290
317
@@ -366,7 +393,7 @@ function Scene(options, fullLayout) {
366
393
this . convertAnnotations = Registry . getComponentMethod ( 'annotations3d' , 'convert' ) ;
367
394
this . drawAnnotations = Registry . getComponentMethod ( 'annotations3d' , 'draw' ) ;
368
395
369
- initializeGLPlot ( this , this . pixelRatio ) ;
396
+ initializeGLPlot ( this ) ;
370
397
}
371
398
372
399
var proto = Scene . prototype ;
@@ -390,16 +417,15 @@ proto.recoverContext = function() {
390
417
var scene = this ;
391
418
var gl = this . glplot . gl ;
392
419
var canvas = this . glplot . canvas ;
393
- var camera = this . glplot . camera ;
394
- var pixelRatio = this . glplot . pixelRatio ;
420
+
395
421
this . glplot . dispose ( ) ;
396
422
397
423
function tryRecover ( ) {
398
424
if ( gl . isContextLost ( ) ) {
399
425
requestAnimationFrame ( tryRecover ) ;
400
426
return ;
401
427
}
402
- if ( ! initializeGLPlot ( scene , camera , pixelRatio , canvas , gl ) ) {
428
+ if ( ! initializeGLPlot ( scene , canvas , gl ) ) {
403
429
Lib . error ( 'Catastrophic and unrecoverable WebGL error. Context lost.' ) ;
404
430
return ;
405
431
}
@@ -496,7 +522,7 @@ proto.plot = function(sceneData, fullLayout, layout) {
496
522
this . spikeOptions . merge ( fullSceneLayout ) ;
497
523
498
524
// Update camera and camera mode
499
- this . setCamera ( fullSceneLayout . camera ) ;
525
+ this . setViewport ( fullSceneLayout ) ;
500
526
this . updateFx ( fullSceneLayout . dragmode , fullSceneLayout . hovermode ) ;
501
527
this . camera . enableWheel = this . graphDiv . _context . _scrollZoom . gl3d ;
502
528
@@ -720,8 +746,16 @@ proto.plot = function(sceneData, fullLayout, layout) {
720
746
* Finally assign the computed aspecratio to the glplot module. This will have an effect
721
747
* on the next render cycle.
722
748
*/
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
+ }
725
759
726
760
// Update frame position for multi plots
727
761
var domain = fullSceneLayout . domain || null ;
@@ -751,18 +785,18 @@ proto.destroy = function() {
751
785
this . glplot = null ;
752
786
} ;
753
787
754
- // getOrbitCamera :: plotly_coords -> orbit_camera_coords
788
+ // getCameraArrays :: plotly_coords -> gl-plot3d_coords
755
789
// inverse of getLayoutCamera
756
- function getOrbitCamera ( camera ) {
790
+ function getCameraArrays ( camera ) {
757
791
return [
758
792
[ camera . eye . x , camera . eye . y , camera . eye . z ] ,
759
793
[ camera . center . x , camera . center . y , camera . center . z ] ,
760
794
[ camera . up . x , camera . up . y , camera . up . z ]
761
795
] ;
762
796
}
763
797
764
- // getLayoutCamera :: orbit_camera_coords -> plotly_coords
765
- // inverse of getOrbitCamera
798
+ // getLayoutCamera :: gl-plot3d_coords -> plotly_coords
799
+ // inverse of getCameraArrays
766
800
function getLayoutCamera ( camera ) {
767
801
return {
768
802
up : { x : camera . up [ 0 ] , y : camera . up [ 1 ] , z : camera . up [ 2 ] } ,
@@ -772,24 +806,25 @@ function getLayoutCamera(camera) {
772
806
} ;
773
807
}
774
808
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 ( ) {
777
811
this . glplot . camera . view . recalcMatrix ( this . camera . view . lastT ( ) ) ;
778
812
return getLayoutCamera ( this . glplot . camera ) ;
779
813
} ;
780
814
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 ) ;
784
821
785
822
var newOrtho = ( cameraData . projection . type === 'orthographic' ) ;
786
823
var oldOrtho = this . glplot . camera . _ortho ;
787
824
788
825
if ( newOrtho !== oldOrtho ) {
789
826
this . glplot . redraw ( ) ;
790
827
791
- var pixelRatio = this . glplot . pixelRatio ;
792
-
793
828
var RGBA = this . glplot . clearColor ;
794
829
this . glplot . gl . clearColor (
795
830
RGBA [ 0 ] , RGBA [ 1 ] , RGBA [ 2 ] , RGBA [ 3 ]
@@ -801,32 +836,30 @@ proto.setCamera = function setCamera(cameraData) {
801
836
802
837
this . glplot . dispose ( ) ;
803
838
804
- initializeGLPlot ( this , pixelRatio ) ;
839
+ initializeGLPlot ( this ) ;
805
840
this . glplot . camera . _ortho = newOrtho ;
806
841
}
807
842
} ;
808
843
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 ) {
812
845
var cameraData = this . getCamera ( ) ;
813
846
var cameraNestedProp = Lib . nestedProperty ( layout , this . id + '.camera' ) ;
814
847
var cameraDataLastSave = cameraNestedProp . get ( ) ;
815
- var hasChanged = false ;
816
848
817
849
function same ( x , y , i , j ) {
818
850
var vectors = [ 'up' , 'center' , 'eye' ] ;
819
851
var components = [ 'x' , 'y' , 'z' ] ;
820
852
return y [ vectors [ i ] ] && ( x [ vectors [ i ] ] [ components [ j ] ] === y [ vectors [ i ] ] [ components [ j ] ] ) ;
821
853
}
822
854
855
+ var changed = false ;
823
856
if ( cameraDataLastSave === undefined ) {
824
- hasChanged = true ;
857
+ changed = true ;
825
858
} else {
826
859
for ( var i = 0 ; i < 3 ; i ++ ) {
827
860
for ( var j = 0 ; j < 3 ; j ++ ) {
828
861
if ( ! same ( cameraData , cameraDataLastSave , i , j ) ) {
829
- hasChanged = true ;
862
+ changed = true ;
830
863
break ;
831
864
}
832
865
}
@@ -835,19 +868,75 @@ proto.saveCamera = function saveCamera(layout) {
835
868
if ( ! cameraDataLastSave . projection || (
836
869
cameraData . projection &&
837
870
cameraData . projection . type !== cameraDataLastSave . projection . type ) ) {
838
- hasChanged = true ;
871
+ changed = true ;
839
872
}
840
873
}
841
874
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 ;
842
907
if ( hasChanged ) {
843
908
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
+ }
845
923
Registry . call ( '_storeDirectGUIEdit' , layout , fullLayout . _preGUI , preGUI ) ;
846
924
847
- cameraNestedProp . set ( cameraData ) ;
925
+ if ( cameraChanged ) {
926
+ cameraNestedProp . set ( cameraData ) ;
848
927
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
+ }
851
940
}
852
941
853
942
return hasChanged ;
0 commit comments