]> gitweb.ps.run Git - cloth_sim/blob - Scripts/OrbitControls.js
tweaking parameters
[cloth_sim] / Scripts / OrbitControls.js
1 // This set of controls performs orbiting, dollying (zooming), and panning.
2 // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
3 //
4 //    Orbit - left mouse / touch: one-finger move
5 //    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
6 //    Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
7
8 THREE.OrbitControls = function ( object, domElement ) {
9
10         if ( domElement === undefined ) console.warn( 'THREE.OrbitControls: The second parameter "domElement" is now mandatory.' );
11         if ( domElement === document ) console.error( 'THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
12
13         this.object = object;
14         this.domElement = domElement;
15
16         // Set to false to disable this control
17         this.enabled = true;
18
19         // "target" sets the location of focus, where the object orbits around
20         this.target = new THREE.Vector3();
21
22         // How far you can dolly in and out ( PerspectiveCamera only )
23         this.minDistance = 0;
24         this.maxDistance = Infinity;
25
26         // How far you can zoom in and out ( OrthographicCamera only )
27         this.minZoom = 0;
28         this.maxZoom = Infinity;
29
30         // How far you can orbit vertically, upper and lower limits.
31         // Range is 0 to Math.PI radians.
32         this.minPolarAngle = 0; // radians
33         this.maxPolarAngle = Math.PI; // radians
34
35         // How far you can orbit horizontally, upper and lower limits.
36         // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI )
37         this.minAzimuthAngle = - Infinity; // radians
38         this.maxAzimuthAngle = Infinity; // radians
39
40         // Set to true to enable damping (inertia)
41         // If damping is enabled, you must call controls.update() in your animation loop
42         this.enableDamping = false;
43         this.dampingFactor = 0.05;
44
45         // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
46         // Set to false to disable zooming
47         this.enableZoom = true;
48         this.zoomSpeed = 1.0;
49
50         // Set to false to disable rotating
51         this.enableRotate = true;
52         this.rotateSpeed = 1.0;
53
54         // Set to false to disable panning
55         this.enablePan = true;
56         this.panSpeed = 1.0;
57         this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up
58         this.keyPanSpeed = 7.0; // pixels moved per arrow key push
59
60         // Set to true to automatically rotate around the target
61         // If auto-rotate is enabled, you must call controls.update() in your animation loop
62         this.autoRotate = false;
63         this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60
64
65         // The four arrow keys
66         this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
67
68         // Mouse buttons
69         this.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN };
70
71         // Touch fingers
72         this.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN };
73
74         // for reset
75         this.target0 = this.target.clone();
76         this.position0 = this.object.position.clone();
77         this.zoom0 = this.object.zoom;
78
79         // the target DOM element for key events
80         this._domElementKeyEvents = null;
81
82         //
83         // public methods
84         //
85
86         this.getPolarAngle = function () {
87
88                 return spherical.phi;
89
90         };
91
92         this.getAzimuthalAngle = function () {
93
94                 return spherical.theta;
95
96         };
97
98         this.listenToKeyEvents = function ( domElement ) {
99
100                 domElement.addEventListener( 'keydown', onKeyDown );
101                 this._domElementKeyEvents = domElement;
102
103         };
104
105         this.saveState = function () {
106
107                 scope.target0.copy( scope.target );
108                 scope.position0.copy( scope.object.position );
109                 scope.zoom0 = scope.object.zoom;
110
111         };
112
113         this.reset = function () {
114
115                 scope.target.copy( scope.target0 );
116                 scope.object.position.copy( scope.position0 );
117                 scope.object.zoom = scope.zoom0;
118
119                 scope.object.updateProjectionMatrix();
120                 scope.dispatchEvent( changeEvent );
121
122                 scope.update();
123
124                 state = STATE.NONE;
125
126         };
127
128         // this method is exposed, but perhaps it would be better if we can make it private...
129         this.update = function () {
130
131                 var offset = new THREE.Vector3();
132
133                 // so camera.up is the orbit axis
134                 var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
135                 var quatInverse = quat.clone().invert();
136
137                 var lastPosition = new THREE.Vector3();
138                 var lastQuaternion = new THREE.Quaternion();
139
140                 var twoPI = 2 * Math.PI;
141
142                 return function update() {
143
144                         var position = scope.object.position;
145
146                         offset.copy( position ).sub( scope.target );
147
148                         // rotate offset to "y-axis-is-up" space
149                         offset.applyQuaternion( quat );
150
151                         // angle from z-axis around y-axis
152                         spherical.setFromVector3( offset );
153
154                         if ( scope.autoRotate && state === STATE.NONE ) {
155
156                                 rotateLeft( getAutoRotationAngle() );
157
158                         }
159
160                         if ( scope.enableDamping ) {
161
162                                 spherical.theta += sphericalDelta.theta * scope.dampingFactor;
163                                 spherical.phi += sphericalDelta.phi * scope.dampingFactor;
164
165                         } else {
166
167                                 spherical.theta += sphericalDelta.theta;
168                                 spherical.phi += sphericalDelta.phi;
169
170                         }
171
172                         // restrict theta to be between desired limits
173
174                         var min = scope.minAzimuthAngle;
175                         var max = scope.maxAzimuthAngle;
176
177                         if ( isFinite( min ) && isFinite( max ) ) {
178
179                                 if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI;
180
181                                 if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI;
182
183                                 if ( min <= max ) {
184
185                                         spherical.theta = Math.max( min, Math.min( max, spherical.theta ) );
186
187                                 } else {
188
189                                         spherical.theta = ( spherical.theta > ( min + max ) / 2 ) ?
190                                                 Math.max( min, spherical.theta ) :
191                                                 Math.min( max, spherical.theta );
192
193                                 }
194
195                         }
196
197                         // restrict phi to be between desired limits
198                         spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
199
200                         spherical.makeSafe();
201
202
203                         spherical.radius *= scale;
204
205                         // restrict radius to be between desired limits
206                         spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
207
208                         // move target to panned location
209
210                         if ( scope.enableDamping === true ) {
211
212                                 scope.target.addScaledVector( panOffset, scope.dampingFactor );
213
214                         } else {
215
216                                 scope.target.add( panOffset );
217
218                         }
219
220                         offset.setFromSpherical( spherical );
221
222                         // rotate offset back to "camera-up-vector-is-up" space
223                         offset.applyQuaternion( quatInverse );
224
225                         position.copy( scope.target ).add( offset );
226
227                         scope.object.lookAt( scope.target );
228
229                         if ( scope.enableDamping === true ) {
230
231                                 sphericalDelta.theta *= ( 1 - scope.dampingFactor );
232                                 sphericalDelta.phi *= ( 1 - scope.dampingFactor );
233
234                                 panOffset.multiplyScalar( 1 - scope.dampingFactor );
235
236                         } else {
237
238                                 sphericalDelta.set( 0, 0, 0 );
239
240                                 panOffset.set( 0, 0, 0 );
241
242                         }
243
244                         scale = 1;
245
246                         // update condition is:
247                         // min(camera displacement, camera rotation in radians)^2 > EPS
248                         // using small-angle approximation cos(x/2) = 1 - x^2 / 8
249
250                         if ( zoomChanged ||
251                                 lastPosition.distanceToSquared( scope.object.position ) > EPS ||
252                                 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
253
254                                 scope.dispatchEvent( changeEvent );
255
256                                 lastPosition.copy( scope.object.position );
257                                 lastQuaternion.copy( scope.object.quaternion );
258                                 zoomChanged = false;
259
260                                 return true;
261
262                         }
263
264                         return false;
265
266                 };
267
268         }();
269
270         this.dispose = function () {
271
272                 scope.domElement.removeEventListener( 'contextmenu', onContextMenu );
273
274                 scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
275                 scope.domElement.removeEventListener( 'wheel', onMouseWheel );
276
277                 scope.domElement.removeEventListener( 'touchstart', onTouchStart );
278                 scope.domElement.removeEventListener( 'touchend', onTouchEnd );
279                 scope.domElement.removeEventListener( 'touchmove', onTouchMove );
280
281                 scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
282                 scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
283
284
285                 if ( scope._domElementKeyEvents !== null ) {
286
287                         scope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown );
288
289                 }
290
291                 //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
292
293         };
294
295         //
296         // internals
297         //
298
299         var scope = this;
300
301         var changeEvent = { type: 'change' };
302         var startEvent = { type: 'start' };
303         var endEvent = { type: 'end' };
304
305         var STATE = {
306                 NONE: - 1,
307                 ROTATE: 0,
308                 DOLLY: 1,
309                 PAN: 2,
310                 TOUCH_ROTATE: 3,
311                 TOUCH_PAN: 4,
312                 TOUCH_DOLLY_PAN: 5,
313                 TOUCH_DOLLY_ROTATE: 6
314         };
315
316         var state = STATE.NONE;
317
318         var EPS = 0.000001;
319
320         // current position in spherical coordinates
321         var spherical = new THREE.Spherical();
322         var sphericalDelta = new THREE.Spherical();
323
324         var scale = 1;
325         var panOffset = new THREE.Vector3();
326         var zoomChanged = false;
327
328         var rotateStart = new THREE.Vector2();
329         var rotateEnd = new THREE.Vector2();
330         var rotateDelta = new THREE.Vector2();
331
332         var panStart = new THREE.Vector2();
333         var panEnd = new THREE.Vector2();
334         var panDelta = new THREE.Vector2();
335
336         var dollyStart = new THREE.Vector2();
337         var dollyEnd = new THREE.Vector2();
338         var dollyDelta = new THREE.Vector2();
339
340         function getAutoRotationAngle() {
341
342                 return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
343
344         }
345
346         function getZoomScale() {
347
348                 return Math.pow( 0.95, scope.zoomSpeed );
349
350         }
351
352         function rotateLeft( angle ) {
353
354                 sphericalDelta.theta -= angle;
355
356         }
357
358         function rotateUp( angle ) {
359
360                 sphericalDelta.phi -= angle;
361
362         }
363
364         var panLeft = function () {
365
366                 var v = new THREE.Vector3();
367
368                 return function panLeft( distance, objectMatrix ) {
369
370                         v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
371                         v.multiplyScalar( - distance );
372
373                         panOffset.add( v );
374
375                 };
376
377         }();
378
379         var panUp = function () {
380
381                 var v = new THREE.Vector3();
382
383                 return function panUp( distance, objectMatrix ) {
384
385                         if ( scope.screenSpacePanning === true ) {
386
387                                 v.setFromMatrixColumn( objectMatrix, 1 );
388
389                         } else {
390
391                                 v.setFromMatrixColumn( objectMatrix, 0 );
392                                 v.crossVectors( scope.object.up, v );
393
394                         }
395
396                         v.multiplyScalar( distance );
397
398                         panOffset.add( v );
399
400                 };
401
402         }();
403
404         // deltaX and deltaY are in pixels; right and down are positive
405         var pan = function () {
406
407                 var offset = new THREE.Vector3();
408
409                 return function pan( deltaX, deltaY ) {
410
411                         var element = scope.domElement;
412
413                         if ( scope.object.isPerspectiveCamera ) {
414
415                                 // perspective
416                                 var position = scope.object.position;
417                                 offset.copy( position ).sub( scope.target );
418                                 var targetDistance = offset.length();
419
420                                 // half of the fov is center to top of screen
421                                 targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
422
423                                 // we use only clientHeight here so aspect ratio does not distort speed
424                                 panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
425                                 panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
426
427                         } else if ( scope.object.isOrthographicCamera ) {
428
429                                 // orthographic
430                                 panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
431                                 panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
432
433                         } else {
434
435                                 // camera neither orthographic nor perspective
436                                 console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
437                                 scope.enablePan = false;
438
439                         }
440
441                 };
442
443         }();
444
445         function dollyOut( dollyScale ) {
446
447                 if ( scope.object.isPerspectiveCamera ) {
448
449                         scale /= dollyScale;
450
451                 } else if ( scope.object.isOrthographicCamera ) {
452
453                         scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
454                         scope.object.updateProjectionMatrix();
455                         zoomChanged = true;
456
457                 } else {
458
459                         console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
460                         scope.enableZoom = false;
461
462                 }
463
464         }
465
466         function dollyIn( dollyScale ) {
467
468                 if ( scope.object.isPerspectiveCamera ) {
469
470                         scale *= dollyScale;
471
472                 } else if ( scope.object.isOrthographicCamera ) {
473
474                         scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
475                         scope.object.updateProjectionMatrix();
476                         zoomChanged = true;
477
478                 } else {
479
480                         console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
481                         scope.enableZoom = false;
482
483                 }
484
485         }
486
487         //
488         // event callbacks - update the object state
489         //
490
491         function handleMouseDownRotate( event ) {
492
493                 rotateStart.set( event.clientX, event.clientY );
494
495         }
496
497         function handleMouseDownDolly( event ) {
498
499                 dollyStart.set( event.clientX, event.clientY );
500
501         }
502
503         function handleMouseDownPan( event ) {
504
505                 panStart.set( event.clientX, event.clientY );
506
507         }
508
509         function handleMouseMoveRotate( event ) {
510
511                 rotateEnd.set( event.clientX, event.clientY );
512
513                 rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
514
515                 var element = scope.domElement;
516
517                 rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
518
519                 rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
520
521                 rotateStart.copy( rotateEnd );
522
523                 scope.update();
524
525         }
526
527         function handleMouseMoveDolly( event ) {
528
529                 dollyEnd.set( event.clientX, event.clientY );
530
531                 dollyDelta.subVectors( dollyEnd, dollyStart );
532
533                 if ( dollyDelta.y > 0 ) {
534
535                         dollyOut( getZoomScale() );
536
537                 } else if ( dollyDelta.y < 0 ) {
538
539                         dollyIn( getZoomScale() );
540
541                 }
542
543                 dollyStart.copy( dollyEnd );
544
545                 scope.update();
546
547         }
548
549         function handleMouseMovePan( event ) {
550
551                 panEnd.set( event.clientX, event.clientY );
552
553                 panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
554
555                 pan( panDelta.x, panDelta.y );
556
557                 panStart.copy( panEnd );
558
559                 scope.update();
560
561         }
562
563         function handleMouseUp( /*event*/ ) {
564
565                 // no-op
566
567         }
568
569         function handleMouseWheel( event ) {
570
571                 if ( event.deltaY < 0 ) {
572
573                         dollyIn( getZoomScale() );
574
575                 } else if ( event.deltaY > 0 ) {
576
577                         dollyOut( getZoomScale() );
578
579                 }
580
581                 scope.update();
582
583         }
584
585         function handleKeyDown( event ) {
586
587                 var needsUpdate = false;
588
589                 switch ( event.keyCode ) {
590
591                         case scope.keys.UP:
592                                 pan( 0, scope.keyPanSpeed );
593                                 needsUpdate = true;
594                                 break;
595
596                         case scope.keys.BOTTOM:
597                                 pan( 0, - scope.keyPanSpeed );
598                                 needsUpdate = true;
599                                 break;
600
601                         case scope.keys.LEFT:
602                                 pan( scope.keyPanSpeed, 0 );
603                                 needsUpdate = true;
604                                 break;
605
606                         case scope.keys.RIGHT:
607                                 pan( - scope.keyPanSpeed, 0 );
608                                 needsUpdate = true;
609                                 break;
610
611                 }
612
613                 if ( needsUpdate ) {
614
615                         // prevent the browser from scrolling on cursor keys
616                         event.preventDefault();
617
618                         scope.update();
619
620                 }
621
622
623         }
624
625         function handleTouchStartRotate( event ) {
626
627                 if ( event.touches.length == 1 ) {
628
629                         rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
630
631                 } else {
632
633                         var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
634                         var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
635
636                         rotateStart.set( x, y );
637
638                 }
639
640         }
641
642         function handleTouchStartPan( event ) {
643
644                 if ( event.touches.length == 1 ) {
645
646                         panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
647
648                 } else {
649
650                         var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
651                         var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
652
653                         panStart.set( x, y );
654
655                 }
656
657         }
658
659         function handleTouchStartDolly( event ) {
660
661                 var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
662                 var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
663
664                 var distance = Math.sqrt( dx * dx + dy * dy );
665
666                 dollyStart.set( 0, distance );
667
668         }
669
670         function handleTouchStartDollyPan( event ) {
671
672                 if ( scope.enableZoom ) handleTouchStartDolly( event );
673
674                 if ( scope.enablePan ) handleTouchStartPan( event );
675
676         }
677
678         function handleTouchStartDollyRotate( event ) {
679
680                 if ( scope.enableZoom ) handleTouchStartDolly( event );
681
682                 if ( scope.enableRotate ) handleTouchStartRotate( event );
683
684         }
685
686         function handleTouchMoveRotate( event ) {
687
688                 if ( event.touches.length == 1 ) {
689
690                         rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
691
692                 } else {
693
694                         var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
695                         var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
696
697                         rotateEnd.set( x, y );
698
699                 }
700
701                 rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
702
703                 var element = scope.domElement;
704
705                 rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
706
707                 rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
708
709                 rotateStart.copy( rotateEnd );
710
711         }
712
713         function handleTouchMovePan( event ) {
714
715                 if ( event.touches.length == 1 ) {
716
717                         panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
718
719                 } else {
720
721                         var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
722                         var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
723
724                         panEnd.set( x, y );
725
726                 }
727
728                 panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
729
730                 pan( panDelta.x, panDelta.y );
731
732                 panStart.copy( panEnd );
733
734         }
735
736         function handleTouchMoveDolly( event ) {
737
738                 var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
739                 var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
740
741                 var distance = Math.sqrt( dx * dx + dy * dy );
742
743                 dollyEnd.set( 0, distance );
744
745                 dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
746
747                 dollyOut( dollyDelta.y );
748
749                 dollyStart.copy( dollyEnd );
750
751         }
752
753         function handleTouchMoveDollyPan( event ) {
754
755                 if ( scope.enableZoom ) handleTouchMoveDolly( event );
756
757                 if ( scope.enablePan ) handleTouchMovePan( event );
758
759         }
760
761         function handleTouchMoveDollyRotate( event ) {
762
763                 if ( scope.enableZoom ) handleTouchMoveDolly( event );
764
765                 if ( scope.enableRotate ) handleTouchMoveRotate( event );
766
767         }
768
769         function handleTouchEnd( /*event*/ ) {
770
771                 // no-op
772
773         }
774
775         //
776         // event handlers - FSM: listen for events and reset state
777         //
778
779         function onPointerDown( event ) {
780
781                 if ( scope.enabled === false ) return;
782
783                 switch ( event.pointerType ) {
784
785                         case 'mouse':
786                         case 'pen':
787                                 onMouseDown( event );
788                                 break;
789
790                         // TODO touch
791
792                 }
793
794         }
795
796         function onPointerMove( event ) {
797
798                 if ( scope.enabled === false ) return;
799
800                 switch ( event.pointerType ) {
801
802                         case 'mouse':
803                         case 'pen':
804                                 onMouseMove( event );
805                                 break;
806
807                         // TODO touch
808
809                 }
810
811         }
812
813         function onPointerUp( event ) {
814
815                 switch ( event.pointerType ) {
816
817                         case 'mouse':
818                         case 'pen':
819                                 onMouseUp( event );
820                                 break;
821
822                         // TODO touch
823
824                 }
825
826         }
827
828         function onMouseDown( event ) {
829
830                 // Prevent the browser from scrolling.
831                 event.preventDefault();
832
833                 // Manually set the focus since calling preventDefault above
834                 // prevents the browser from setting it automatically.
835
836                 scope.domElement.focus ? scope.domElement.focus() : window.focus();
837
838                 var mouseAction;
839
840                 switch ( event.button ) {
841
842                         case 0:
843
844                                 mouseAction = scope.mouseButtons.LEFT;
845                                 break;
846
847                         case 1:
848
849                                 mouseAction = scope.mouseButtons.MIDDLE;
850                                 break;
851
852                         case 2:
853
854                                 mouseAction = scope.mouseButtons.RIGHT;
855                                 break;
856
857                         default:
858
859                                 mouseAction = - 1;
860
861                 }
862
863                 switch ( mouseAction ) {
864
865                         case THREE.MOUSE.DOLLY:
866
867                                 if ( scope.enableZoom === false ) return;
868
869                                 handleMouseDownDolly( event );
870
871                                 state = STATE.DOLLY;
872
873                                 break;
874
875                         case THREE.MOUSE.ROTATE:
876
877                                 if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
878
879                                         if ( scope.enablePan === false ) return;
880
881                                         handleMouseDownPan( event );
882
883                                         state = STATE.PAN;
884
885                                 } else {
886
887                                         if ( scope.enableRotate === false ) return;
888
889                                         handleMouseDownRotate( event );
890
891                                         state = STATE.ROTATE;
892
893                                 }
894
895                                 break;
896
897                         case THREE.MOUSE.PAN:
898
899                                 if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
900
901                                         if ( scope.enableRotate === false ) return;
902
903                                         handleMouseDownRotate( event );
904
905                                         state = STATE.ROTATE;
906
907                                 } else {
908
909                                         if ( scope.enablePan === false ) return;
910
911                                         handleMouseDownPan( event );
912
913                                         state = STATE.PAN;
914
915                                 }
916
917                                 break;
918
919                         default:
920
921                                 state = STATE.NONE;
922
923                 }
924
925                 if ( state !== STATE.NONE ) {
926
927                         scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
928                         scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
929
930                         scope.dispatchEvent( startEvent );
931
932                 }
933
934         }
935
936         function onMouseMove( event ) {
937
938                 if ( scope.enabled === false ) return;
939
940                 event.preventDefault();
941
942                 switch ( state ) {
943
944                         case STATE.ROTATE:
945
946                                 if ( scope.enableRotate === false ) return;
947
948                                 handleMouseMoveRotate( event );
949
950                                 break;
951
952                         case STATE.DOLLY:
953
954                                 if ( scope.enableZoom === false ) return;
955
956                                 handleMouseMoveDolly( event );
957
958                                 break;
959
960                         case STATE.PAN:
961
962                                 if ( scope.enablePan === false ) return;
963
964                                 handleMouseMovePan( event );
965
966                                 break;
967
968                 }
969
970         }
971
972         function onMouseUp( event ) {
973
974                 scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
975                 scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
976
977                 if ( scope.enabled === false ) return;
978
979                 handleMouseUp( event );
980
981                 scope.dispatchEvent( endEvent );
982
983                 state = STATE.NONE;
984
985         }
986
987         function onMouseWheel( event ) {
988
989                 if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
990
991                 event.preventDefault();
992                 event.stopPropagation();
993
994                 scope.dispatchEvent( startEvent );
995
996                 handleMouseWheel( event );
997
998                 scope.dispatchEvent( endEvent );
999
1000         }
1001
1002         function onKeyDown( event ) {
1003
1004                 if ( scope.enabled === false || scope.enablePan === false ) return;
1005
1006                 handleKeyDown( event );
1007
1008         }
1009
1010         function onTouchStart( event ) {
1011
1012                 if ( scope.enabled === false ) return;
1013
1014                 event.preventDefault(); // prevent scrolling
1015
1016                 switch ( event.touches.length ) {
1017
1018                         case 1:
1019
1020                                 switch ( scope.touches.ONE ) {
1021
1022                                         case THREE.TOUCH.ROTATE:
1023
1024                                                 if ( scope.enableRotate === false ) return;
1025
1026                                                 handleTouchStartRotate( event );
1027
1028                                                 state = STATE.TOUCH_ROTATE;
1029
1030                                                 break;
1031
1032                                         case THREE.TOUCH.PAN:
1033
1034                                                 if ( scope.enablePan === false ) return;
1035
1036                                                 handleTouchStartPan( event );
1037
1038                                                 state = STATE.TOUCH_PAN;
1039
1040                                                 break;
1041
1042                                         default:
1043
1044                                                 state = STATE.NONE;
1045
1046                                 }
1047
1048                                 break;
1049
1050                         case 2:
1051
1052                                 switch ( scope.touches.TWO ) {
1053
1054                                         case THREE.TOUCH.DOLLY_PAN:
1055
1056                                                 if ( scope.enableZoom === false && scope.enablePan === false ) return;
1057
1058                                                 handleTouchStartDollyPan( event );
1059
1060                                                 state = STATE.TOUCH_DOLLY_PAN;
1061
1062                                                 break;
1063
1064                                         case THREE.TOUCH.DOLLY_ROTATE:
1065
1066                                                 if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1067
1068                                                 handleTouchStartDollyRotate( event );
1069
1070                                                 state = STATE.TOUCH_DOLLY_ROTATE;
1071
1072                                                 break;
1073
1074                                         default:
1075
1076                                                 state = STATE.NONE;
1077
1078                                 }
1079
1080                                 break;
1081
1082                         default:
1083
1084                                 state = STATE.NONE;
1085
1086                 }
1087
1088                 if ( state !== STATE.NONE ) {
1089
1090                         scope.dispatchEvent( startEvent );
1091
1092                 }
1093
1094         }
1095
1096         function onTouchMove( event ) {
1097
1098                 if ( scope.enabled === false ) return;
1099
1100                 event.preventDefault(); // prevent scrolling
1101                 event.stopPropagation();
1102
1103                 switch ( state ) {
1104
1105                         case STATE.TOUCH_ROTATE:
1106
1107                                 if ( scope.enableRotate === false ) return;
1108
1109                                 handleTouchMoveRotate( event );
1110
1111                                 scope.update();
1112
1113                                 break;
1114
1115                         case STATE.TOUCH_PAN:
1116
1117                                 if ( scope.enablePan === false ) return;
1118
1119                                 handleTouchMovePan( event );
1120
1121                                 scope.update();
1122
1123                                 break;
1124
1125                         case STATE.TOUCH_DOLLY_PAN:
1126
1127                                 if ( scope.enableZoom === false && scope.enablePan === false ) return;
1128
1129                                 handleTouchMoveDollyPan( event );
1130
1131                                 scope.update();
1132
1133                                 break;
1134
1135                         case STATE.TOUCH_DOLLY_ROTATE:
1136
1137                                 if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1138
1139                                 handleTouchMoveDollyRotate( event );
1140
1141                                 scope.update();
1142
1143                                 break;
1144
1145                         default:
1146
1147                                 state = STATE.NONE;
1148
1149                 }
1150
1151         }
1152
1153         function onTouchEnd( event ) {
1154
1155                 if ( scope.enabled === false ) return;
1156
1157                 handleTouchEnd( event );
1158
1159                 scope.dispatchEvent( endEvent );
1160
1161                 state = STATE.NONE;
1162
1163         }
1164
1165         function onContextMenu( event ) {
1166
1167                 if ( scope.enabled === false ) return;
1168
1169                 event.preventDefault();
1170
1171         }
1172
1173         //
1174
1175         scope.domElement.addEventListener( 'contextmenu', onContextMenu );
1176
1177         scope.domElement.addEventListener( 'pointerdown', onPointerDown );
1178         scope.domElement.addEventListener( 'wheel', onMouseWheel );
1179
1180         scope.domElement.addEventListener( 'touchstart', onTouchStart );
1181         scope.domElement.addEventListener( 'touchend', onTouchEnd );
1182         scope.domElement.addEventListener( 'touchmove', onTouchMove );
1183
1184         // force an update at start
1185
1186         this.update();
1187
1188 };
1189
1190 THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
1191 THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
1192
1193
1194 // This set of controls performs orbiting, dollying (zooming), and panning.
1195 // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
1196 // This is very similar to OrbitControls, another set of touch behavior
1197 //
1198 //    Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
1199 //    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
1200 //    Pan - left mouse, or arrow keys / touch: one-finger move
1201
1202 THREE.MapControls = function ( object, domElement ) {
1203
1204         THREE.OrbitControls.call( this, object, domElement );
1205
1206         this.screenSpacePanning = false; // pan orthogonal to world-space direction camera.up
1207
1208         this.mouseButtons.LEFT = THREE.MOUSE.PAN;
1209         this.mouseButtons.RIGHT = THREE.MOUSE.ROTATE;
1210
1211         this.touches.ONE = THREE.TOUCH.PAN;
1212         this.touches.TWO = THREE.TOUCH.DOLLY_ROTATE;
1213
1214 };
1215
1216 THREE.MapControls.prototype = Object.create( THREE.EventDispatcher.prototype );
1217 THREE.MapControls.prototype.constructor = THREE.MapControls;