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