/** * @author mschuetz / http://mschuetz.at * * adapted from THREE.OrbitControls by * * @author qiao / https://github.com/qiao * @author mrdoob / http://mrdoob.com * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author erich666 / http://erichaines.com * * * */ import * as THREE from "../../libs/three.js/build/three.module.js"; import {Buttons} from "../defines.js"; import {Utils} from "../utils.js"; import {EventDispatcher} from "../EventDispatcher.js"; let minRadius = 2 export class OrbitControls extends EventDispatcher{ constructor(viewer){ super(); this.viewer = viewer; this.renderer = viewer.renderer; this.scene = null; this.sceneControls = new THREE.Scene(); this.rotationSpeed = 3; //旋转速度 this.fadeFactor = 100; this.yawDelta = 0; this.pitchDelta = 0; this.panDelta = new THREE.Vector2(0, 0); this.radiusDelta = 0; this.doubleClockZoomEnabled = true; this.tweens = []; this.dollyStart = new THREE.Vector2 this.dollyEnd = new THREE.Vector2 this.keys = { FORWARD: ['W'.charCodeAt(0), 38], BACKWARD: ['S'.charCodeAt(0), 40], LEFT: ['A'.charCodeAt(0), 37], RIGHT: ['D'.charCodeAt(0), 39], UP: ['Q'.charCodeAt(0)], DOWN: ['E'.charCodeAt(0)], }; let drag = (e) => { if(!this.enabled)return let viewport = e.dragViewport; if(!viewport || viewport.camera.type == "OrthographicCamera" )return //let camera = viewport.camera if (e.drag.object !== null) { return; } let mode if(e.isTouch){ if(e.touches.length == 1){ mode = 'rotate' }else{ mode = 'scale-pan' } }else{ mode = e.buttons === Buttons.LEFT ? 'rotate' : 'pan' } if (e.drag.startHandled === undefined) { e.drag.startHandled = true; this.dispatchEvent({type: 'start'}); } let ndrag = e.drag.pointerDelta.clone()//.add(new THREE.Vector2(1,1)).multiplyScalar(0.5) ndrag.y *= -1 if (mode == 'rotate') { this.yawDelta += ndrag.x * this.rotationSpeed; this.pitchDelta += ndrag.y * this.rotationSpeed; } else if(mode == 'pan'){ this.panDelta.x += ndrag.x; this.panDelta.y += ndrag.y; }else if(mode == 'scale-pan'){ //add this.dollyEnd.subVectors(e.touches[0].pointer, e.touches[1].pointer); var scale = this.dollyEnd.length() / this.dollyStart.length() this.dollyStart.copy(this.dollyEnd); this.radiusDelta = (1-scale) * this.scene.view.radius //------------------------ //平移 let pointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点 let delta = new THREE.Vector2().subVectors(pointer, this.lastScalePointer) delta.y *= -1 this.panDelta.add(delta) this.lastScalePointer = pointer.clone() //console.log('scale ',scale, this.radiusDelta) } this.stopTweens(); }; let drop = e => { if(!this.enabled)return this.dispatchEvent({type: 'end'}); }; let scroll = (e) => { if(!this.enabled)return let resolvedRadius = this.scene.view.radius + this.radiusDelta; if(resolvedRadius < 0.1 && e.delta>0)return; //防止缩放太小,导致很慢 this.radiusDelta += -e.delta * resolvedRadius * 0.1; this.stopTweens(); }; let dblclick = (e) => { if(!this.enabled)return if(this.doubleClockZoomEnabled){ this.zoomToLocation(e.mouse); } }; let previousTouch = null; let touchStart = e => { previousTouch = e; }; let touchEnd = e => { previousTouch = e; }; let touchMove = e => { if(!this.enabled)return if (e.touches.length === 2 && previousTouch.touches.length === 2){ let prev = previousTouch; let curr = e; let prevDX = prev.touches[0].pageX - prev.touches[1].pageX; let prevDY = prev.touches[0].pageY - prev.touches[1].pageY; let prevDist = Math.sqrt(prevDX * prevDX + prevDY * prevDY); let currDX = curr.touches[0].pageX - curr.touches[1].pageX; let currDY = curr.touches[0].pageY - curr.touches[1].pageY; let currDist = Math.sqrt(currDX * currDX + currDY * currDY); let delta = currDist / prevDist; let resolvedRadius = this.scene.view.radius + this.radiusDelta; let newRadius = resolvedRadius / delta; this.radiusDelta = newRadius - resolvedRadius; this.stopTweens(); }else if(e.touches.length === 3 && previousTouch.touches.length === 3){ let prev = previousTouch; let curr = e; let prevMeanX = (prev.touches[0].pageX + prev.touches[1].pageX + prev.touches[2].pageX) / 3; let prevMeanY = (prev.touches[0].pageY + prev.touches[1].pageY + prev.touches[2].pageY) / 3; let currMeanX = (curr.touches[0].pageX + curr.touches[1].pageX + curr.touches[2].pageX) / 3; let currMeanY = (curr.touches[0].pageY + curr.touches[1].pageY + curr.touches[2].pageY) / 3; let delta = { x: (currMeanX - prevMeanX) / this.renderer.domElement.clientWidth, y: (currMeanY - prevMeanY) / this.renderer.domElement.clientHeight }; this.panDelta.x += delta.x; this.panDelta.y += delta.y; this.stopTweens(); } previousTouch = e; }; this.addEventListener('touchstart', touchStart); this.addEventListener('touchend', touchEnd); this.addEventListener('touchmove', touchMove); this.viewer.addEventListener('global_drag', drag); this.viewer.addEventListener('global_drop', drop); this.viewer.addEventListener('global_mousewheel', scroll); this.viewer.addEventListener('global_dblclick', dblclick); this.viewer.addEventListener('global_touchmove', (e)=>{ if(e.touches.length>1){//单指的就触发上一句 //console.log('global_touchmove' ) drag(e) } }); let prepareScale = (e)=>{//触屏的scale this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer); this.lastScalePointer = new THREE.Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点 } this.viewer.addEventListener('global_touchstart', (e)=>{ if(this.enabled && e.touches.length==2){//只监听开头两个指头 prepareScale(e) } }) /* this.viewer.addEventListener('global_touchend', (e)=>{ if(!this.enabled)return if(e.touches.length==1){//停止scale,开始rotate prepareRotate(e) //this.pointerDragStart = null //console.log('只剩一个', e.pointer.toArray()) } }) */ this.viewer.addEventListener('focusOnObject',(o)=>{ if(o.position && o.CamTarget){ let distance = o.position.distanceTo(o.CamTarget) if(distance < minRadius) minRadius = distance * 0.5 //融合页面当focus一个很小的物体时,需要将minRadius也调小 } }) } setScene (scene) { this.scene = scene; } setEnable(enabled){ this.enabled = enabled } stop(){ this.yawDelta = 0; this.pitchDelta = 0; this.radiusDelta = 0; this.panDelta.set(0, 0); } /* zoomToLocation(mouse){ if(!this.enabled)return let camera = this.scene.getActiveCamera(); let I = Utils.getMousePointCloudIntersection( null, mouse, this.viewer.inputHandler.pointer, camera, this.viewer, this.scene.pointclouds, {pickClipped: true}); if (I === null) { return; } let targetRadius = 0; { let minimumJumpDistance = 0.2; let domElement = this.renderer.domElement; let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer , camera, domElement.clientWidth, domElement.clientHeight); let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray); let lastNode = nodes[nodes.length - 1]; let radius = lastNode.getBoundingSphere(new THREE.Sphere()).radius; targetRadius = Math.min(this.scene.view.radius, radius); targetRadius = Math.max(minimumJumpDistance, targetRadius); } let d = this.scene.view.direction.multiplyScalar(-1); let cameraTargetPosition = new THREE.Vector3().addVectors(I.location, d.multiplyScalar(targetRadius)); // TODO Unused: let controlsTargetPosition = I.location; let animationDuration = 600; let easing = TWEEN.Easing.Quartic.Out; { // animate let value = {x: 0}; let tween = new TWEEN.Tween(value).to({x: 1}, animationDuration); tween.easing(easing); this.tweens.push(tween); let startPos = this.scene.view.position.clone(); let targetPos = cameraTargetPosition.clone(); let startRadius = this.scene.view.radius; let targetRadius = cameraTargetPosition.distanceTo(I.location); tween.onUpdate(() => { let t = value.x; this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x; this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y; this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z; this.scene.view.radius = (1 - t) * startRadius + t * targetRadius; this.viewer.setMoveSpeed(this.scene.view.radius); }); tween.onComplete(() => { this.tweens = this.tweens.filter(e => e !== tween); }); tween.start(); } } */ zoomToLocation(mouse){ let I = viewer.inputHandler.intersect; if(!I)return let object = I.object || I.pointcloud; I = I.location if(!I || !object)return; let dis = this.scene.view.position.distanceTo(I); let bound = object.boundingBox.clone().applyMatrix4(object.matrixWorld) let size = bound.getSize(new THREE.Vector3); let len = size.length() let distance = THREE.Math.clamp(dis, 0.1, Math.max(len * 0.1, 3) ); minRadius = distance viewer.focusOnObject({ position:I }, 'point', null, {distance}) } stopTweens () { this.tweens.forEach(e => e.stop()); this.tweens = []; } update (delta) { if(!this.enabled)return let view = this.scene.view; { // accelerate while input is given let ih = this.viewer.inputHandler; let moveForward = this.keys.FORWARD.some(e => ih.pressedKeys[e]); let moveBackward = this.keys.BACKWARD.some(e => ih.pressedKeys[e]); let moveLeft = this.keys.LEFT.some(e => ih.pressedKeys[e]); let moveRight = this.keys.RIGHT.some(e => ih.pressedKeys[e]); let moveUp = this.keys.UP.some(e => ih.pressedKeys[e]); let moveDown = this.keys.DOWN.some(e => ih.pressedKeys[e]); let px = 0 , py = 0, pz = 0 if(moveForward){ py = 1 }else if(moveBackward){ py = -1 } if(moveLeft){ px = -1 }else if(moveRight){ px = 1 } if(moveUp){ pz = 1 }else if(moveDown){ pz = -1 } (px!=0 || py!=0 || pz!=0) && view.translate(px, py, pz, true); } { // apply rotation let progression = Math.min(1, this.fadeFactor * delta); let yaw = view.yaw; let pitch = view.pitch; let pivot = view.getPivot(); yaw -= progression * this.yawDelta; pitch -= progression * this.pitchDelta; view.yaw = yaw; view.pitch = pitch; let V = this.scene.view.direction.multiplyScalar(-view.radius); let position = new THREE.Vector3().addVectors(pivot, V); view.position.copy(position); } { // apply pan /* let progression = Math.min(1, this.fadeFactor * delta); let panDistance = progression * view.radius * 3; */ let camera = this.scene.getActiveCamera() let panDistance = 2 * view.radius * Math.tan(THREE.Math.degToRad(camera.fov / 2));//参照4dkk。 平移target(也就是平移镜头位置),但还是难以保证跟手(navvis也不一定跟手,但是很奇怪在居中时中心点居然是跟手的,可能计算方式不同) //计算了下确实是这么算的。 平移pivot。 let px = -this.panDelta.x * panDistance; let py = this.panDelta.y * panDistance; view.pan(px, py); } { // apply zoom let progression = 1//Math.min(1, this.fadeFactor * delta); // let radius = view.radius + progression * this.radiusDelta * view.radius * 0.1; let radius = view.radius + progression * this.radiusDelta; let V = view.direction.multiplyScalar(-radius); let position = new THREE.Vector3().addVectors(view.getPivot(), V); if(this.constantlyForward) {// 到达中心点后还能继续向前移动,也就是能推进中心点 if(radius < minRadius){ radius = minRadius } } view.radius = radius; view.position.copy(position); } { let speed = view.radius; this.viewer.setMoveSpeed(speed); } { // decelerate over time /* let progression = Math.min(1, this.fadeFactor * delta); let attenuation = Math.max(0, 1 - this.fadeFactor * delta); this.yawDelta *= attenuation; this.pitchDelta *= attenuation; this.panDelta.multiplyScalar(attenuation); // this.radiusDelta *= attenuation; this.radiusDelta -= progression * this.radiusDelta; */ //取消衰减,直接stop this.stop() } } };