/** * @author mschuetz / http://mschuetz.at * * */ import * as THREE from "../../libs/three.js/build/three.module.js"; import {KeyCodes} from "../KeyCodes.js"; import {Utils} from "../utils.js"; import {MOUSE} from "../defines.js"; import {EventDispatcher} from "../EventDispatcher.js"; export class InputHandler extends EventDispatcher { constructor (viewer,scene) { super(); this.viewer = viewer; this.renderer = viewer.renderer; this.domElement = this.renderer.domElement; this.enabled = true; this.scene = scene; this.interactiveScenes = []; this.interactiveObjects = new Set(); this.inputListeners = []; this.blacklist = new Set(); this.drag = null; this.mouse = new THREE.Vector2(0, 0); //add: this.pointer = new THREE.Vector2(0, 0); //交互点的屏幕坐标,有别于DOM坐标,在此存放NDC坐标。(NDC,三维常用坐标系,二维坐标,整个屏幕映射范围(-1,1),屏幕中心为原点,+Y朝上,+X朝右) this.mouseDownMouse = new THREE.Vector2(0, 0); this.selection = []; this.hoveredElements = []; this.pressedKeys = {}; this.wheelDelta = 0; this.speed = 1; this.logMessages = false; if (this.domElement.tabIndex === -1) { this.domElement.tabIndex = 2222; } this.domElement.addEventListener('contextmenu', (event) => { event.preventDefault(); }, false); this.domElement.addEventListener('click', this.onMouseClick.bind(this), false); this.domElement.addEventListener('mousedown', this.onMouseDown.bind(this), false); window.addEventListener('mouseup', this.onMouseUp.bind(this), false); this.domElement.addEventListener('mousemove', this.onMouseMove.bind(this), false); //add /* this.domElement.addEventListener("pointerout", this.onMouseUp.bind(this)), this.domElement.addEventListener("pointercancel", this.onMouseUp.bind(this)), */ this.domElement.addEventListener('mousewheel', this.onMouseWheel.bind(this), false); this.domElement.addEventListener('DOMMouseScroll', this.onMouseWheel.bind(this), false); // Firefox this.domElement.addEventListener('dblclick', this.onDoubleClick.bind(this)); this.domElement.addEventListener('keydown', this.onKeyDown.bind(this)); this.domElement.addEventListener('keyup', this.onKeyUp.bind(this)); this.domElement.addEventListener('touchstart', this.onTouchStart.bind(this)); this.domElement.addEventListener('touchend', this.onTouchEnd.bind(this)); this.domElement.addEventListener('touchmove', this.onTouchMove.bind(this)); } /* addInputListener (listener) { this.inputListeners.push(listener); } removeInputListener (listener) { this.inputListeners = this.inputListeners.filter(e => e !== listener); } getSortedListeners(){ return this.inputListeners.sort( (a, b) => { let ia = (a.importance !== undefined) ? a.importance : 0; let ib = (b.importance !== undefined) ? b.importance : 0; return ib - ia; }); } */ onTouchStart (e) { if (this.logMessages) console.log(this.constructor.name + ': onTouchStart'); e.preventDefault(); if (e.touches.length === 1) { let rect = this.domElement.getBoundingClientRect(); let x = e.touches[0].pageX - rect.left; let y = e.touches[0].pageY - rect.top; this.mouse.set(x, y); this.startDragging(null); } /* for (let inputListener of this.getSortedListeners()) { inputListener */this.viewer.dispatchEvent({ type: /* "global_"+ */e.type, // global_ 是否加上 touches: e.touches, changedTouches: e.changedTouches }); //} //add this.mouseDownMouse = this.mouse.clone() } onTouchEnd (e) { if (this.logMessages) console.log(this.constructor.name + ': onTouchEnd'); e.preventDefault(); /* for (let inputListener of this.getSortedListeners()) { inputListener. */this.viewer.dispatchEvent({ type: 'global_drop', drag: this.drag, viewer: this.viewer }); //} this.drag = null; /* for (let inputListener of this.getSortedListeners()) { inputListener */this.viewer.dispatchEvent({ type: 'global_' + e.type, touches: e.touches, changedTouches: e.changedTouches }); //} } onTouchMove (e) { if (this.logMessages) console.log(this.constructor.name + ': onTouchMove'); e.preventDefault(); if (e.touches.length === 1) { let rect = this.domElement.getBoundingClientRect(); let x = e.touches[0].pageX - rect.left; let y = e.touches[0].pageY - rect.top; this.mouse.set(x, y); if (this.drag) { this.drag.mouse = 1; //why??非左键也非右键的意思? this.drag.mouseDelta.x = x - this.drag.end.x; this.drag.mouseDelta.y = y - this.drag.end.y; this.drag.end.set(x, y); if (this.logMessages) console.log(this.constructor.name + ': drag: '); /* for (let inputListener of this.getSortedListeners()) { inputListener */this.viewer.dispatchEvent({ type: 'global_drag', drag: this.drag, viewer: this.viewer }); //} } } /* for (let inputListener of this.getSortedListeners()) { inputListener. */this.viewer.dispatchEvent({ type: e.type, touches: e.touches, changedTouches: e.changedTouches }); //} // DEBUG CODE // let debugTouches = [...e.touches, { // pageX: this.domElement.clientWidth / 2, // pageY: this.domElement.clientHeight / 2}]; // for(let inputListener of this.getSortedListeners()){ // inputListener.dispatchEvent({ // type: e.type, // touches: debugTouches, // changedTouches: e.changedTouches // }); // } } onKeyDown (e) { if (this.logMessages) console.log(this.constructor.name + ': onKeyDown'); // DELETE if (e.keyCode === KeyCodes.DELETE && this.selection.length > 0) { this.dispatchEvent({ type: 'delete', selection: this.selection }); this.deselectAll(); } this.dispatchEvent({ type: 'keydown', keyCode: e.keyCode, event: e }); // for(let l of this.getSortedListeners()){ // l.dispatchEvent({ // type: "keydown", // keyCode: e.keyCode, // event: e // }); // } this.pressedKeys[e.keyCode] = true; // e.preventDefault(); } onKeyUp (e) { if (this.logMessages) console.log(this.constructor.name + ': onKeyUp'); delete this.pressedKeys[e.keyCode]; e.preventDefault(); } onDoubleClick (e) { if (this.logMessages) console.log(this.constructor.name + ': onDoubleClick'); let consumed = false; for (let hovered of this.hoveredElements) { if (hovered._listeners && hovered._listeners['dblclick']) { hovered.object.dispatchEvent({ type: 'dblclick', mouse: this.mouse, object: hovered.object }); consumed = true; break; } } if (!consumed) { /* for (let inputListener of this.getSortedListeners()) { inputListener. */this.viewer.dispatchEvent({ type: 'global_dblclick', mouse: this.mouse, object: null }); //} } e.preventDefault(); } onMouseClick (e) { if (this.logMessages) console.log(this.constructor.name + ': onMouseClick'); e.preventDefault(); } onMouseDown (e) { if (this.logMessages) console.log(this.constructor.name + ': onMouseDown'); //重新获取一下pointer, 因点击了浏览器的按钮展开列表时 move回来不会触发onmousemove,所以pointer是旧的 var { camera, viewport } = this.getPointerInViewport(e.clientX, e.clientY ) this.hoverViewport = viewport if(!viewport)return e.preventDefault(); //this.onMouseMove(e)//add 重新获取一下mouse, 因在此前canvas可能失去侦听(不记得是什么了。如果一定要的话再写个加侦听的函数,但是直接调用mousemove的话会发送drag,导致magnifier停止渲染) let consumed = false; let consume = () => { return consumed = true; }; if (this.hoveredElements.length === 0) { /* for (let inputListener of this.getSortedListeners()) { inputListener */this.viewer.dispatchEvent({ type: 'global_mousedown', viewer: this.viewer, mouse: this.mouse }); //} }else{ for(let hovered of this.hoveredElements){ let object = hovered.object; object.dispatchEvent({ type: 'mousedown', viewer: this.viewer, consume: consume }); if(consumed){ break; } } } if (!this.drag) { let target = e.button == THREE.MOUSE.LEFT && this.hoveredElements.find(el => (//只有左键能拖拽 el.object._listeners && el.object._listeners['drag'] && el.object._listeners['drag'].length > 0)); if (target) { this.startDragging(target.object, {location: target.point}); } else { this.startDragging(null); } } //add this.drag.pointerDragStart = this.pointer.clone() this.drag.intersectStart = this.intersectPoint; this.mouseDownMouse = this.mouse.clone() this.dragViewport = this.hoverViewport; } onMouseUp (e) { if(!this.drag){// 在canvas外mousedown return } if (this.logMessages) console.log(this.constructor.name + ': onMouseUp'); e.preventDefault(); let pressDistance = this.mouseDownMouse.distanceTo(this.mouse); let noMovement = this.drag.pointerDelta.length() == 0//this.getNormalizedDrag().length() === 0; let consumed = false; let consume = () => { return consumed = true; }; //if (this.hoveredElements.length === 0) { /* for (let inputListener of this.getSortedListeners()) { inputListener */this.viewer.dispatchEvent({ type: 'global_mouseup', viewer: this.viewer, mouse: this.mouse, consume: consume, drag :this.drag, pressDistance }); /* if(consumed){//?? break; } */ //} //} if (this.hoveredElements.length > 0) { let hovered = this.hoveredElements .map(e => e.object) .find(e => (e._listeners && e._listeners['mouseup'])); if(hovered){ hovered.dispatchEvent({ type: 'mouseup', viewer: this.viewer, consume: consume }); } } if (this.drag) { //拖拽结束 if (this.drag.object/* && e.button == THREE.MOUSE.LEFT */) {//add LEFT if (this.logMessages) console.log(`${this.constructor.name}: drop ${this.drag.object.name}`); this.drag.object.dispatchEvent({ type: 'drop', drag: this.drag, viewer: this.viewer, pressDistance, button : e.button,//add 放开的鼠标按键 isAtDomElement: e.target == this.domElement, viewport:this.dragViewport }); } else { /* for (let inputListener of this.getSortedListeners()) { inputListener. */this.viewer.dispatchEvent({ type: 'global_drop', drag: this.drag, viewer: this.viewer, pressDistance, isAtDomElement: e.target == this.domElement, button: e.button //add }); //} // check for a click if(pressDistance < Potree.config.clickMaxDragDis){ if(this.hoveredElements && this.hoveredElements[0]){ if (this.logMessages) console.log(`${this.constructor.name}: click ${clicked.name}`); this.hoveredElements[0].object.dispatchEvent({ type: 'click', viewer: this.viewer, consume: consume, viewport: this.hoverViewport, isAtDomElement: e.target == this.domElement, button: e.button //add }); }else{ /* for (let inputListener of this.getSortedListeners()) { inputListener. */this.viewer.dispatchEvent({ type: 'global_click', drag: this.drag, viewer: this.viewer, pressDistance, viewport: this.hoverViewport, isAtDomElement: e.target == this.domElement, button: e.button //add }); //} } } } /* let clicked = this.hoveredElements.map(h => h.object).find(v => v === this.drag.object) !== undefined; if(clicked){ if (this.logMessages) console.log(`${this.constructor.name}: click ${this.drag.object.name}`); this.drag.object.dispatchEvent({ type: 'click', viewer: this.viewer, consume: consume, }); } */ this.drag = null; } this.dragViewport = null if(!consumed && !this.fixSelection){ if (e.button === THREE.MOUSE.LEFT) { if (noMovement) { let selectable = this.hoveredElements .find(el => el.object._listeners && el.object._listeners['select']); if (selectable) { selectable = selectable.object; if (this.isSelected(selectable)) { this.selection .filter(e => e !== selectable) .forEach(e => this.toggleSelection(e)); } else { this.deselectAll(); this.toggleSelection(selectable); } } else { this.deselectAll(); } } } else if ((e.button === THREE.MOUSE.RIGHT) && noMovement) { this.deselectAll(); } } } getPointerInViewport(clientX, clientY, viewForceAt ){ let rect = this.domElement.getBoundingClientRect(); let x = clientX - rect.left; let y = clientY - rect.top; let camera let viewport //if(this.viewer.viewports || viewForceAt){ var getDimension = (view)=>{ var left = Math.floor(this.domElement.clientWidth * view.left) , bottom = this.domElement.clientHeight * view.bottom , width = Math.floor(this.domElement.clientWidth * view.width) , height = this.domElement.clientHeight * view.height , top = Math.floor(this.domElement.clientHeight - bottom - height) return {left, bottom, width, height, top} } var getView = (view, left, bottom, width, height, top)=>{ this.mouse.set(x-left, y - top ) Utils.convertScreenPositionToNDC(this.pointer, this.mouse, width, height); camera = view.camera; viewport = view } if(viewForceAt){ let {left, bottom, width, height, top} = getDimension(viewForceAt) getView(viewForceAt, left, bottom, width, height, top) }else{ var length = this.viewer.viewports.length; //var getif = false for(var i=0;i= left && x <= left + width && y >= top && y <= top + height){ getView(view, left, bottom, width, height, top) //getif = true break; } } } /*} else{ camera = this.viewer.scene.getActiveCamera() this.mouse.set(x , y ) Utils.convertScreenPositionToNDC(this.pointer, this.mouse, this.domElement.clientWidth, this.domElement.clientHeight); } */ return { camera, viewport } } onMouseMove (e) { var { camera, viewport } = this.getPointerInViewport(e.clientX, e.clientY, this.dragViewport) this.hoverViewport = viewport if(!viewport)return//刚变化viewport时会找不到 let hoveredElements = this.getHoveredElements(); if(hoveredElements.length > 0){ let names = hoveredElements.map(h => h.object.name).join(", "); if (this.logMessages) console.log(`${this.constructor.name}: onMouseMove; hovered: '${names}'`); } //add let intersectPoint = viewport.noPointcloud? null : Utils.getMousePointCloudIntersection( viewport, this.mouse, this.pointer, camera, this.viewer, this.viewer.scene.pointclouds, {pickClipped: true}); if(viewport.camera.type == 'OrthographicCamera'/* == 'mapViewport' */){ let pos3d = new THREE.Vector3(this.pointer.x,this.pointer.y,-1).unproject(viewport.camera); //z:-1朝外 pos3d.setZ(viewer.bound.boundingBox.min.z + 0.2) //大概放地面上 if(!intersectPoint){ intersectPoint = {} } intersectPoint.orthoIntersect = pos3d.clone() } if(e.onlyGetIntersect){ return intersectPoint } e.preventDefault(); if (this.drag) {//有拖拽(不一定拖拽了物体, 也不一定按下了鼠标) this.drag.mouse = e.buttons; this.drag.hoverViewport = this.hoverViewport /* this.drag.mouseDelta.x = this.mouse.x - this.drag.end.x; this.drag.mouseDelta.y = this.mouse.y - this.drag.end.y; */ this.drag.pointerDelta.set(this.pointer.x - this.drag.end.x, this.pointer.y - this.drag.end.y ) this.drag.end.set(this.pointer.x, this.pointer.y); if (this.drag.object && (e.buttons == MOUSE.NONE || !this.drag.notPressMouse )){//add notPressMouse 如果标记是不需要按鼠标的拖拽,则一旦按下鼠标,就暂停拖拽物体(改为拖拽场景):(如添加测量时突然拖拽画面) if (this.logMessages) console.log(this.constructor.name + ': drag: ' + this.drag.object.name); this.drag.object.dispatchEvent({ type: 'drag', drag: this.drag, viewer: this.viewer, intersectPoint }); } else { //add: this.drag.pointer = this.pointer.clone(); if (this.logMessages) console.log(this.constructor.name + ': drag: '); let dragConsumed = false; /* for (let inputListener of this.getSortedListeners()) { inputListener. */this.viewer.dispatchEvent({ type: 'global_drag', drag: this.drag, viewer: this.viewer, intersectPoint, consume: () => {dragConsumed = true;} }); /* if(dragConsumed){ break; } */ //} } }else{ let curr = hoveredElements.map(a => a.object).find(a => true); let prev = this.hoveredElements.map(a => a.object).find(a => true); if(curr !== prev){ if(curr){ if (this.logMessages) console.log(`${this.constructor.name}: mouseover: ${curr.name}`); curr.dispatchEvent({ type: 'mouseover', object: curr, }); } if(prev){ if (this.logMessages) console.log(`${this.constructor.name}: mouseleave: ${prev.name}`); prev.dispatchEvent({ type: 'mouseleave', object: prev, }); } } if(hoveredElements.length > 0){ let object = hoveredElements .map(e => e.object) .find(e => (e._listeners && e._listeners['mousemove'])); if(object){ object.dispatchEvent({ type: 'mousemove', object: object }); } } //仅在鼠标不按下时更新: { let handleState = viewer.modules.Alignment.handleState if(viewport.alignment && handleState && viewport.alignment[handleState]){ if(handleState == 'translate'){ if( intersectPoint && intersectPoint.location ){ viewer.dispatchEvent({ type : "CursorChange", action : "add", name:"movePointcloud" }) }else{ viewer.dispatchEvent({ type : "CursorChange", action : "remove", name:"movePointcloud" }) } }else if(handleState == 'rotate'){ if( intersectPoint && intersectPoint.location ){ viewer.dispatchEvent({ type : "CursorChange", action : "add", name:"rotatePointcloud" }) }else{ viewer.dispatchEvent({ type : "CursorChange", action : "remove", name:"rotatePointcloud" }) } } } } } if (intersectPoint) { if(viewer.showCoordType){ //显示坐标位置时 let pos = intersectPoint.point.position.toArray() if(viewer.showCoordType == "local"){ }else if(viewer.showCoordType == "lonlat"){ pos = viewer.transform.lonlatToLocal.inverse(pos) }else{ pos = viewer.transform.lonlatToLocal.inverse(pos) pos = viewer.transform.lonlatTo4550.forward(pos) } viewer.dispatchEvent({ type : "coordinateChange", pos }) } } this.intersectPoint = intersectPoint intersectPoint && (this.hoverViewport.lastIntersect = intersectPoint) /* for (let inputListener of this.getSortedListeners()) { inputListener. */this.viewer.dispatchEvent({ type: 'global_mousemove', intersectPoint, hoverViewport:this.hoverViewport, buttons: e.buttons, drag:this.drag, }); //} this.hoveredElements = hoveredElements; } onMouseWheel(e){ if(!this.enabled) return; if(this.logMessages) console.log(this.constructor.name + ": onMouseWheel"); e.preventDefault(); let delta = 0; if (e.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9 delta = e.wheelDelta; } else if (e.detail !== undefined) { // Firefox delta = -e.detail; } let ndelta = Math.sign(delta); // this.wheelDelta += Math.sign(delta); if (this.hoveredElement) { this.hoveredElement.object.dispatchEvent({ type: 'mousewheel', delta: ndelta, object: this.hoveredElement.object }); } else { /* for (let inputListener of this.getSortedListeners()) { inputListener. */this.viewer.dispatchEvent({ type: 'global_mousewheel', delta: ndelta, object: null, hoverViewport: this.hoverViewport }); //} } } startDragging (object, args = null) { let name = object ? object.name : "no name"; if (this.logMessages) console.log(`${this.constructor.name}: startDragging: '${name}'`); this.drag = { start: this.pointer.clone(), end: this.pointer.clone(), pointerDelta: new THREE.Vector2(0, 0), object: object, dragViewport : this.hoverViewport, //开始之后就不会变了 hoverViewport: this.hoverViewport //会变化 }; this.viewer.dispatchEvent({ type: "startDragging", drag: this.drag, hoverViewport: this.hoverViewport }) /* console.log('startDragging') console.log(this.drag.end) */ if (args) { for (let key of Object.keys(args)) { this.drag[key] = args[key]; } } } /* getMousePointCloudIntersection (mouse) { return Utils.getMousePointCloudIntersection( this.mouse, this.scene.getActiveCamera(), this.viewer, this.scene.pointclouds); } */ toggleSelection (object) { let oldSelection = this.selection; let index = this.selection.indexOf(object); if (index === -1) { this.selection.push(object); object.dispatchEvent({ type: 'select' }); } else { this.selection.splice(index, 1); object.dispatchEvent({ type: 'deselect' }); } this.dispatchEvent({ type: 'selection_changed', oldSelection: oldSelection, selection: this.selection }); } deselect(object){ let oldSelection = this.selection; let index = this.selection.indexOf(object); if(index >= 0){ this.selection.splice(index, 1); object.dispatchEvent({ type: 'deselect' }); this.dispatchEvent({ type: 'selection_changed', oldSelection: oldSelection, selection: this.selection }); } } deselectAll () { for (let object of this.selection) { object.dispatchEvent({ type: 'deselect' }); } let oldSelection = this.selection; if (this.selection.length > 0) { this.selection = []; this.dispatchEvent({ type: 'selection_changed', oldSelection: oldSelection, selection: this.selection }); } } isSelected (object) { let index = this.selection.indexOf(object); return index !== -1; } registerInteractiveObject(object){ this.interactiveObjects.add(object); } removeInteractiveObject(object){ this.interactiveObjects.delete(object); } registerInteractiveScene (scene) { let index = this.interactiveScenes.indexOf(scene); if (index === -1) { this.interactiveScenes.push(scene); } } unregisterInteractiveScene (scene) { let index = this.interactiveScenes.indexOf(scene); if (index > -1) { this.interactiveScenes.splice(index, 1); } } getHoveredElement () { let hoveredElements = this.getHoveredElements(); if (hoveredElements.length > 0) { return hoveredElements[0]; } else { return null; } } getHoveredElements () { let scenes = this.hoverViewport.interactiveScenes || this.interactiveScenes.concat(this.scene); let interactableListeners = ['mouseup', 'mousemove', 'mouseover', 'mouseleave', 'drag', 'drop', 'click', 'select', 'deselect']; let interactables = []; for (let scene of scenes) { scene.traverseVisible(node => {//检测加了侦听的object if (node._listeners && node.visible && !this.blacklist.has(node)) { let hasInteractableListener = interactableListeners.filter((e) => { return node._listeners[e] !== undefined; }).length > 0; if (hasInteractableListener) { interactables.push(node); } } }); } let camera = this.hoverViewport.camera let ray = Utils.mouseToRay(this.pointer, camera ); let raycaster = new THREE.Raycaster(); raycaster.ray.set(ray.origin, ray.direction); raycaster.camera = camera //add raycaster.params.Line.threshold = 0.2; //raycaster.layers.enableAll()//add viewer.setCameraLayers(raycaster, //设置能识别到的layers(如空间模型里只有mapViewer能识别到marker) ['sceneObjects','mapObjects','measure','marker','transformationTool'], this.hoverViewport && this.hoverViewport.extraEnableLayers ) viewer.emit('raycaster', {viewport: this.hoverViewport})//add let intersections = raycaster.intersectObjects(interactables.filter(o => o.visible), true); //原本是false 检测不到children intersections = intersections.map(e=>{//add 转化为interactables var object = e.object; do{ if(interactables.includes(object)) { e.oriObject = e.object; e.object = object; break } object = object.parent }while(object) return e }) //add for测量线,在检测到sphere时优先选中sphere而非线 intersections = intersections.sort(function(a,b){return b.object.renderOrder-a.object.renderOrder}) // 降序 return intersections; } /* setScene (scene) { this.deselectAll(); this.scene = scene; } */ update (delta) { } /*getNormalizedDrag () { if (!this.drag) { return new THREE.Vector2(0, 0); } let diff = new THREE.Vector2().subVectors(this.drag.end, this.drag.start); diff.x = diff.x / this.domElement.clientWidth; diff.y = diff.y / this.domElement.clientHeight; return diff; } getNormalizedLastDrag () { if (!this.drag) { return new THREE.Vector2(0, 0); } let mouseDelta = this.drag.mouseDelta.clone(); mouseDelta.x = mouseDelta.x / this.domElement.clientWidth; mouseDelta.y = mouseDelta.y / this.domElement.clientHeight; return mouseDelta; } */ getMouseDirection() {//add let camera = this.hoverViewport.camera var t = new THREE.Vector3(this.pointer.x, this.pointer.y, -1).unproject(camera), i = new THREE.Vector3(this.pointer.x, this.pointer.y, 1).unproject(camera); return {origin: t, direction:i.clone().sub(t).normalize() } } }