OrbitControls.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /**
  2. * @author mschuetz / http://mschuetz.at
  3. *
  4. * adapted from THREE.OrbitControls by
  5. *
  6. * @author qiao / https://github.com/qiao
  7. * @author mrdoob / http://mrdoob.com
  8. * @author alteredq / http://alteredqualia.com/
  9. * @author WestLangley / http://github.com/WestLangley
  10. * @author erich666 / http://erichaines.com
  11. *
  12. *
  13. *
  14. */
  15. import * as THREE from "../../libs/three.js/build/three.module.js";
  16. import {Buttons} from "../defines.js";
  17. import {Utils} from "../utils.js";
  18. import {EventDispatcher} from "../EventDispatcher.js";
  19. let minRadius = 2
  20. export class OrbitControls extends EventDispatcher{
  21. constructor(viewer){
  22. super();
  23. this.viewer = viewer;
  24. this.renderer = viewer.renderer;
  25. this.scene = null;
  26. this.sceneControls = new THREE.Scene();
  27. this.rotationSpeed = 3; //旋转速度
  28. this.fadeFactor = 100;
  29. this.yawDelta = 0;
  30. this.pitchDelta = 0;
  31. this.panDelta = new THREE.Vector2(0, 0);
  32. this.radiusDelta = 0;
  33. this.doubleClockZoomEnabled = true;
  34. this.tweens = [];
  35. this.dollyStart = new THREE.Vector2
  36. this.dollyEnd = new THREE.Vector2
  37. this.keys = {
  38. FORWARD: ['W'.charCodeAt(0), 38],
  39. BACKWARD: ['S'.charCodeAt(0), 40],
  40. LEFT: ['A'.charCodeAt(0), 37],
  41. RIGHT: ['D'.charCodeAt(0), 39],
  42. UP: ['Q'.charCodeAt(0)],
  43. DOWN: ['E'.charCodeAt(0)],
  44. };
  45. let drag = (e) => {
  46. if(!this.enabled)return
  47. let viewport = e.dragViewport;
  48. if(!viewport || viewport.camera.type == "OrthographicCamera" )return
  49. //let camera = viewport.camera
  50. if (e.drag.object !== null) {
  51. return;
  52. }
  53. let mode
  54. if(e.isTouch){
  55. if(e.touches.length == 1){
  56. mode = 'rotate'
  57. }else{
  58. mode = 'scale-pan'
  59. }
  60. }else{
  61. mode = e.buttons === Buttons.LEFT ? 'rotate' : 'pan'
  62. }
  63. if (e.drag.startHandled === undefined) {
  64. e.drag.startHandled = true;
  65. this.dispatchEvent({type: 'start'});
  66. }
  67. let ndrag = e.drag.pointerDelta.clone()//.add(new THREE.Vector2(1,1)).multiplyScalar(0.5)
  68. ndrag.y *= -1
  69. if (mode == 'rotate') {
  70. this.yawDelta += ndrag.x * this.rotationSpeed;
  71. this.pitchDelta += ndrag.y * this.rotationSpeed;
  72. } else if(mode == 'pan'){
  73. this.panDelta.x += ndrag.x;
  74. this.panDelta.y += ndrag.y;
  75. }else if(mode == 'scale-pan'){ //add
  76. this.dollyEnd.subVectors(e.touches[0].pointer, e.touches[1].pointer);
  77. var scale = this.dollyEnd.length() / this.dollyStart.length()
  78. this.dollyStart.copy(this.dollyEnd);
  79. this.radiusDelta = (1-scale) * this.scene.view.radius
  80. //------------------------
  81. //平移
  82. let pointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
  83. let delta = new THREE.Vector2().subVectors(pointer, this.lastScalePointer)
  84. delta.y *= -1
  85. this.panDelta.add(delta)
  86. this.lastScalePointer = pointer.clone()
  87. //console.log('scale ',scale, this.radiusDelta)
  88. }
  89. this.stopTweens();
  90. };
  91. let drop = e => {
  92. if(!this.enabled)return
  93. this.dispatchEvent({type: 'end'});
  94. };
  95. let scroll = (e) => {
  96. if(!this.enabled)return
  97. let resolvedRadius = this.scene.view.radius + this.radiusDelta;
  98. if(resolvedRadius < 0.1 && e.delta>0)return; //防止缩放太小,导致很慢
  99. this.radiusDelta += -e.delta * resolvedRadius * 0.1;
  100. this.stopTweens();
  101. };
  102. let dblclick = (e) => {
  103. if(!this.enabled)return
  104. if(this.doubleClockZoomEnabled){
  105. this.zoomToLocation(e.mouse);
  106. }
  107. };
  108. let previousTouch = null;
  109. let touchStart = e => {
  110. previousTouch = e;
  111. };
  112. let touchEnd = e => {
  113. previousTouch = e;
  114. };
  115. let touchMove = e => {
  116. if(!this.enabled)return
  117. if (e.touches.length === 2 && previousTouch.touches.length === 2){
  118. let prev = previousTouch;
  119. let curr = e;
  120. let prevDX = prev.touches[0].pageX - prev.touches[1].pageX;
  121. let prevDY = prev.touches[0].pageY - prev.touches[1].pageY;
  122. let prevDist = Math.sqrt(prevDX * prevDX + prevDY * prevDY);
  123. let currDX = curr.touches[0].pageX - curr.touches[1].pageX;
  124. let currDY = curr.touches[0].pageY - curr.touches[1].pageY;
  125. let currDist = Math.sqrt(currDX * currDX + currDY * currDY);
  126. let delta = currDist / prevDist;
  127. let resolvedRadius = this.scene.view.radius + this.radiusDelta;
  128. let newRadius = resolvedRadius / delta;
  129. this.radiusDelta = newRadius - resolvedRadius;
  130. this.stopTweens();
  131. }else if(e.touches.length === 3 && previousTouch.touches.length === 3){
  132. let prev = previousTouch;
  133. let curr = e;
  134. let prevMeanX = (prev.touches[0].pageX + prev.touches[1].pageX + prev.touches[2].pageX) / 3;
  135. let prevMeanY = (prev.touches[0].pageY + prev.touches[1].pageY + prev.touches[2].pageY) / 3;
  136. let currMeanX = (curr.touches[0].pageX + curr.touches[1].pageX + curr.touches[2].pageX) / 3;
  137. let currMeanY = (curr.touches[0].pageY + curr.touches[1].pageY + curr.touches[2].pageY) / 3;
  138. let delta = {
  139. x: (currMeanX - prevMeanX) / this.renderer.domElement.clientWidth,
  140. y: (currMeanY - prevMeanY) / this.renderer.domElement.clientHeight
  141. };
  142. this.panDelta.x += delta.x;
  143. this.panDelta.y += delta.y;
  144. this.stopTweens();
  145. }
  146. previousTouch = e;
  147. };
  148. this.addEventListener('touchstart', touchStart);
  149. this.addEventListener('touchend', touchEnd);
  150. this.addEventListener('touchmove', touchMove);
  151. this.viewer.addEventListener('global_drag', drag);
  152. this.viewer.addEventListener('global_drop', drop);
  153. this.viewer.addEventListener('global_mousewheel', scroll);
  154. this.viewer.addEventListener('global_dblclick', dblclick);
  155. this.viewer.addEventListener('global_touchmove', (e)=>{
  156. if(e.touches.length>1){//单指的就触发上一句
  157. //console.log('global_touchmove' )
  158. drag(e)
  159. }
  160. });
  161. let prepareScale = (e)=>{//触屏的scale
  162. this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer);
  163. this.lastScalePointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
  164. }
  165. this.viewer.addEventListener('global_touchstart', (e)=>{
  166. if(this.enabled && e.touches.length==2){//只监听开头两个指头
  167. prepareScale(e)
  168. }
  169. })
  170. /* this.viewer.addEventListener('global_touchend', (e)=>{
  171. if(!this.enabled)return
  172. if(e.touches.length==1){//停止scale,开始rotate
  173. prepareRotate(e)
  174. //this.pointerDragStart = null
  175. //console.log('只剩一个', e.pointer.toArray())
  176. }
  177. }) */
  178. this.viewer.addEventListener('focusOnObject',(o)=>{
  179. if(o.position && o.CamTarget){
  180. let distance = o.position.distanceTo(o.CamTarget)
  181. if(distance < minRadius) minRadius = distance * 0.5 //融合页面当focus一个很小的物体时,需要将minRadius也调小
  182. }
  183. })
  184. }
  185. setScene (scene) {
  186. this.scene = scene;
  187. }
  188. setEnable(enabled){
  189. this.enabled = enabled
  190. }
  191. stop(){
  192. this.yawDelta = 0;
  193. this.pitchDelta = 0;
  194. this.radiusDelta = 0;
  195. this.panDelta.set(0, 0);
  196. }
  197. /* zoomToLocation(mouse){
  198. if(!this.enabled)return
  199. let camera = this.scene.getActiveCamera();
  200. let I = Utils.getMousePointCloudIntersection(
  201. null, mouse, this.viewer.inputHandler.pointer,
  202. camera,
  203. this.viewer,
  204. this.scene.pointclouds,
  205. {pickClipped: true});
  206. if (I === null) {
  207. return;
  208. }
  209. let targetRadius = 0;
  210. {
  211. let minimumJumpDistance = 0.2;
  212. let domElement = this.renderer.domElement;
  213. let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer , camera, domElement.clientWidth, domElement.clientHeight);
  214. let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray);
  215. let lastNode = nodes[nodes.length - 1];
  216. let radius = lastNode.getBoundingSphere(new THREE.Sphere()).radius;
  217. targetRadius = Math.min(this.scene.view.radius, radius);
  218. targetRadius = Math.max(minimumJumpDistance, targetRadius);
  219. }
  220. let d = this.scene.view.direction.multiplyScalar(-1);
  221. let cameraTargetPosition = new THREE.Vector3().addVectors(I.location, d.multiplyScalar(targetRadius));
  222. // TODO Unused: let controlsTargetPosition = I.location;
  223. let animationDuration = 600;
  224. let easing = TWEEN.Easing.Quartic.Out;
  225. { // animate
  226. let value = {x: 0};
  227. let tween = new TWEEN.Tween(value).to({x: 1}, animationDuration);
  228. tween.easing(easing);
  229. this.tweens.push(tween);
  230. let startPos = this.scene.view.position.clone();
  231. let targetPos = cameraTargetPosition.clone();
  232. let startRadius = this.scene.view.radius;
  233. let targetRadius = cameraTargetPosition.distanceTo(I.location);
  234. tween.onUpdate(() => {
  235. let t = value.x;
  236. this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x;
  237. this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y;
  238. this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z;
  239. this.scene.view.radius = (1 - t) * startRadius + t * targetRadius;
  240. this.viewer.setMoveSpeed(this.scene.view.radius);
  241. });
  242. tween.onComplete(() => {
  243. this.tweens = this.tweens.filter(e => e !== tween);
  244. });
  245. tween.start();
  246. }
  247. } */
  248. zoomToLocation(mouse){
  249. let I = viewer.inputHandler.intersect;
  250. if(!I)return
  251. let object = I.object || I.pointcloud;
  252. I = I.location
  253. if(!I || !object)return;
  254. let dis = this.scene.view.position.distanceTo(I);
  255. let bound = object.boundingBox.clone().applyMatrix4(object.matrixWorld)
  256. let size = bound.getSize(new THREE.Vector3);
  257. let len = size.length()
  258. let distance = THREE.Math.clamp(dis, 0.1, Math.max(len * 0.1, 3) );
  259. minRadius = distance
  260. viewer.focusOnObject({ position:I }, 'point', null, {distance})
  261. }
  262. stopTweens () {
  263. this.tweens.forEach(e => e.stop());
  264. this.tweens = [];
  265. }
  266. update (delta) {
  267. if(!this.enabled)return
  268. let view = this.scene.view;
  269. { // accelerate while input is given
  270. let ih = this.viewer.inputHandler;
  271. let moveForward = this.keys.FORWARD.some(e => ih.pressedKeys[e]);
  272. let moveBackward = this.keys.BACKWARD.some(e => ih.pressedKeys[e]);
  273. let moveLeft = this.keys.LEFT.some(e => ih.pressedKeys[e]);
  274. let moveRight = this.keys.RIGHT.some(e => ih.pressedKeys[e]);
  275. let moveUp = this.keys.UP.some(e => ih.pressedKeys[e]);
  276. let moveDown = this.keys.DOWN.some(e => ih.pressedKeys[e]);
  277. let px = 0 , py = 0, pz = 0
  278. if(moveForward){
  279. py = 1
  280. }else if(moveBackward){
  281. py = -1
  282. }
  283. if(moveLeft){
  284. px = -1
  285. }else if(moveRight){
  286. px = 1
  287. }
  288. if(moveUp){
  289. pz = 1
  290. }else if(moveDown){
  291. pz = -1
  292. }
  293. (px!=0 || py!=0 || pz!=0) && view.translate(px, py, pz, true);
  294. }
  295. { // apply rotation
  296. let progression = Math.min(1, this.fadeFactor * delta);
  297. let yaw = view.yaw;
  298. let pitch = view.pitch;
  299. let pivot = view.getPivot();
  300. yaw -= progression * this.yawDelta;
  301. pitch -= progression * this.pitchDelta;
  302. view.yaw = yaw;
  303. view.pitch = pitch;
  304. let V = this.scene.view.direction.multiplyScalar(-view.radius);
  305. let position = new THREE.Vector3().addVectors(pivot, V);
  306. view.position.copy(position);
  307. }
  308. { // apply pan
  309. /* let progression = Math.min(1, this.fadeFactor * delta);
  310. let panDistance = progression * view.radius * 3; */
  311. let camera = this.scene.getActiveCamera()
  312. let panDistance = 2 * view.radius * Math.tan(THREE.Math.degToRad(camera.fov / 2));//参照4dkk。 平移target(也就是平移镜头位置),但还是难以保证跟手(navvis也不一定跟手,但是很奇怪在居中时中心点居然是跟手的,可能计算方式不同)
  313. //计算了下确实是这么算的。 平移pivot。
  314. let px = -this.panDelta.x * panDistance;
  315. let py = this.panDelta.y * panDistance;
  316. view.pan(px, py);
  317. }
  318. { // apply zoom
  319. let progression = 1//Math.min(1, this.fadeFactor * delta);
  320. // let radius = view.radius + progression * this.radiusDelta * view.radius * 0.1;
  321. let radius = view.radius + progression * this.radiusDelta;
  322. let V = view.direction.multiplyScalar(-radius);
  323. let position = new THREE.Vector3().addVectors(view.getPivot(), V);
  324. if(this.constantlyForward) {// 到达中心点后还能继续向前移动,也就是能推进中心点
  325. if(radius < minRadius){
  326. radius = minRadius
  327. }
  328. }
  329. view.radius = radius;
  330. view.position.copy(position);
  331. }
  332. {
  333. let speed = view.radius;
  334. this.viewer.setMoveSpeed(speed);
  335. }
  336. { // decelerate over time
  337. /* let progression = Math.min(1, this.fadeFactor * delta);
  338. let attenuation = Math.max(0, 1 - this.fadeFactor * delta);
  339. this.yawDelta *= attenuation;
  340. this.pitchDelta *= attenuation;
  341. this.panDelta.multiplyScalar(attenuation);
  342. // this.radiusDelta *= attenuation;
  343. this.radiusDelta -= progression * this.radiusDelta; */
  344. //取消衰减,直接stop
  345. this.stop()
  346. }
  347. }
  348. };