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.