PageSourceSearch

https://genesisglobalgrp.com/lib/three/orbit-controls.js

js genesisglobalgrp.com collected 2026-10-06 15:13:32 UTC 27,269 bytes, 1,287 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.