DragControls.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * @author zz85 / https://github.com/zz85
  3. * Running this will allow you to drag three.js objects around the screen.
  4. */
  5. THREE.DragControls = function( _camera, _objects, _domElement ) {
  6. var _projector = new THREE.Projector();
  7. var _raycaster = new THREE.Raycaster();
  8. var _mouse = new THREE.Vector3(),
  9. _offset = new THREE.Vector3();
  10. var _selected, _hovered;
  11. var p3subp1 = new THREE.Vector3();
  12. var targetposition = new THREE.Vector3();
  13. var zerovector = new THREE.Vector3();
  14. this.enabled = false;
  15. /* Custom Event Handling */
  16. var _listeners = {
  17. };
  18. var me = this;
  19. this.on = function( event, handler ) {
  20. if ( ! _listeners[ event ] ) _listeners[ event ] = [];
  21. _listeners[ event ].push( handler );
  22. return me;
  23. };
  24. this.off = function( event, handler ) {
  25. var l = _listeners[ event ];
  26. if ( ! l ) return me;
  27. if ( l.indexOf( handler ) > - 1 ) {
  28. l.splice( handler, 1 );
  29. }
  30. return me;
  31. };
  32. var notify = function( event, data, member ) {
  33. var l = _listeners[ event ];
  34. if ( ! l ) return;
  35. if ( ! member ) {
  36. for ( var i = 0; i < l.length; i ++ ) {
  37. l[ i ]( data );
  38. }
  39. }
  40. };
  41. this.setObjects = function( objects ) {
  42. if ( objects instanceof THREE.Scene ) {
  43. _objects = objects.children;
  44. } else {
  45. _objects = objects;
  46. }
  47. };
  48. this.setObjects( _objects );
  49. this.activate = function() {
  50. _domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
  51. _domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
  52. _domElement.addEventListener( 'mouseup', onDocumentMouseUp, false );
  53. };
  54. this.deactivate = function() {
  55. _domElement.removeEventListener( 'mousemove', onDocumentMouseMove, false );
  56. _domElement.removeEventListener( 'mousedown', onDocumentMouseDown, false );
  57. _domElement.removeEventListener( 'mouseup', onDocumentMouseUp, false );
  58. };
  59. this.dispose = function() {
  60. me.deactivate();
  61. };
  62. this.activate();
  63. function onDocumentMouseMove( event ) {
  64. event.preventDefault();
  65. _mouse.x = ( event.clientX / _domElement.width ) * 2 - 1;
  66. _mouse.y = - ( event.clientY / _domElement.height ) * 2 + 1;
  67. _raycaster.setFromCamera( _mouse, _camera );
  68. var ray = _raycaster.ray;
  69. if ( _selected && me.enabled ) {
  70. var normal = _selected.normal;
  71. // I found this article useful about plane-line intersections
  72. // http://paulbourke.net/geometry/planeline/
  73. var denom = normal.dot( ray.direction );
  74. if ( denom == 0 ) {
  75. // bail
  76. console.log( 'no or infinite solutions' );
  77. return;
  78. }
  79. var num = normal.dot( p3subp1.copy( _selected.point ).sub( ray.origin ) );
  80. var u = num / denom;
  81. targetposition.copy( ray.direction ).multiplyScalar( u ).add( ray.origin ).sub( _offset );
  82. // _selected.object.position.copy(targetposition);
  83. var xLock, yLock, zLock = false;
  84. var moveX, moveY, moveZ;
  85. if ( xLock ) {
  86. moveX = true;
  87. moveY = false;
  88. moveZ = false;
  89. } else if ( yLock ) {
  90. moveX = false;
  91. moveY = true;
  92. moveZ = false;
  93. } else {
  94. moveX = moveY = moveZ = true;
  95. }
  96. // Reverse Matrix?
  97. if ( moveX ) _selected.object.position.x = targetposition.x;
  98. if ( moveY ) _selected.object.position.y = targetposition.y;
  99. if ( moveZ ) _selected.object.position.z = targetposition.z;
  100. notify( 'drag', _selected );
  101. return;
  102. }
  103. _raycaster.setFromCamera( _mouse, _camera );
  104. var intersects = _raycaster.intersectObjects( _objects );
  105. if ( intersects.length > 0 ) {
  106. _domElement.style.cursor = 'pointer';
  107. _hovered = intersects[ 0 ];
  108. notify( 'hoveron', _hovered );
  109. } else {
  110. notify( 'hoveroff', _hovered );
  111. _hovered = null;
  112. _domElement.style.cursor = 'auto';
  113. }
  114. }
  115. function onDocumentMouseDown( event ) {
  116. event.preventDefault();
  117. _mouse.x = ( event.clientX / _domElement.width ) * 2 - 1;
  118. _mouse.y = - ( event.clientY / _domElement.height ) * 2 + 1;
  119. _raycaster.setFromCamera( _mouse, _camera );
  120. var intersects = _raycaster.intersectObjects( _objects );
  121. var ray = _raycaster.ray;
  122. var normal = ray.direction; // normal ray to the camera position
  123. if ( intersects.length > 0 ) {
  124. _selected = intersects[ 0 ];
  125. _selected.ray = ray;
  126. _selected.normal = normal ;
  127. _offset.copy( _selected.point ).sub( _selected.object.position );
  128. _domElement.style.cursor = 'move';
  129. notify( 'dragstart', _selected );
  130. }
  131. }
  132. function onDocumentMouseUp( event ) {
  133. event.preventDefault();
  134. if ( _selected ) {
  135. notify( 'dragend', _selected );
  136. _selected = null;
  137. }
  138. _domElement.style.cursor = 'auto';
  139. }
  140. }