
function PanoramaControls(camera, domElement) {

  
    // fyz 相机放大缩小
    this.activationThreshold = 1.1;
    this.scrollZoomSpeed = 0.08;
    this.scrollZoomSta = true;
    this.zoomMin = 0.7;
    this.zoomMax = 1.5;
    this.baseFov = 70;
 
    this.camera = camera;
    this.camera.fov = this.baseFov;
     
    this.domElement = domElement; 
    this.camera.controls = this; 
    this.enabled = true;

    this.target = new THREE.Vector3(0, 0, 0);
    this.lookVector = new THREE.Vector3;
    this.aimFrom = this.camera.position;


    this.lat = 0;
    this.latMin = -60//-40;
    this.latMax = 60//40;
    this.lon = 0;
    this.phi = 0;
    this.theta = 0;
    this.lookSpeed = 0.05;
    this.rotationAcc = new THREE.Vector2;
    this.rotationSpeed = new THREE.Vector2;
    this.rotationHistory = [];
    this.rotationDifference = new THREE.Vector2;


    this.pointerDragOn = !1;
    this.pointer = new THREE.Vector3(0, 0, -1);
    this.pointerDragStart = new THREE.Vector3(0, 0, -1); 
    this._wheel = 0;
    this.zoomLevel = 1; 
    this.translationWorldDelta = new THREE.Vector3
    this.bindEvents()
}

PanoramaControls.prototype.bindEvents = function() {

    window.addEventListener("mousemove", this.onMouseMove.bind(this));
    this.domElement.addEventListener("mousedown", this.onMouseDown.bind(this));
    window.addEventListener("mouseup", this.onMouseUp.bind(this));
    this.domElement.addEventListener("mouseover", (event) => this.pointerDragOn && 0 === event.which && this.onMouseUp(event));

    this.domElement.addEventListener("touchstart", this.onTouchStart.bind(this));
    this.domElement.addEventListener("touchmove", this.onTouchMove.bind(this));
    this.domElement.addEventListener("touchend", this.onTouchEnd.bind(this));

    this.domElement.addEventListener("wheel", this.onMouseWheel.bind(this));   // fyz wheel事件代替mousewheel事件
    this.domElement.addEventListener("DOMMouseScroll", this.onMouseWheel.bind(this));
    this.domElement.addEventListener("contextmenu", (event) => event.preventDefault());

    //document.addEventListener("keydown", this.onKeyDown.bind(this));
    //document.addEventListener("keyup", this.onKeyUp.bind(this));

}
PanoramaControls.prototype.lookAt = function(point) {
 

    var directionNegative = this.camera.position.clone().sub(point),
        theta = Math.atan(directionNegative.z / directionNegative.x);

    theta += directionNegative.x < 0 ? Math.PI : 0;
    theta += directionNegative.x > 0 && directionNegative.z < 0 ? 2 * Math.PI : 0;
    this.lon = THREE.Math.radToDeg(theta) + 180;

    let projectorR = Math.sqrt(directionNegative.x * directionNegative.x + directionNegative.z * directionNegative.z),
        phi = Math.atan(directionNegative.y / projectorR);

    this.lat = -THREE.Math.radToDeg(phi)
}
PanoramaControls.prototype.startRotationFrom = function(screenX, screenY) {

    this.updatePointer(screenX, screenY);
    this.pointerDragOn = true;
    this.pointerDragStart.copy(this.pointer);
    //TODO
    //this.pointerDragStartIntersect = this.player.getMouseIntersect(this.pointer.clone(), [this.scene.skybox]).point;
    this.rotationHistory = [];
    this.rotationSpeed.set(0, 0);
}
PanoramaControls.prototype.onTouchStart = function(event) {

    if (this.enabled) {

        event.preventDefault();
        event.stopPropagation();
        this.startRotationFrom(event.changedTouches[0].clientX, event.changedTouches[0].clientY);
    }

}
PanoramaControls.prototype.onMouseDown = function(event) {

    if (this.enabled) {

        event.preventDefault();
        event.stopPropagation()

        switch (event.button) {

            case 0:
                this.startRotationFrom(event.clientX, event.clientY);
                break;
            case 2:

        }
    }
}

PanoramaControls.prototype.updateRotation = function( ) {


    if (this.enabled && this.pointerDragOn) {

        var pointerDragStart3D = new THREE.Vector3(this.pointerDragStart.x, this.pointerDragStart.y, 1).unproject(this.camera);
        var pointer3D = new THREE.Vector3(this.pointer.x, this.pointer.y, 1).unproject(this.camera);

            //两交互点分别到原点的长度
        var pointerDragStart3DLength = Math.sqrt(pointerDragStart3D.x * pointerDragStart3D.x + pointerDragStart3D.z * pointerDragStart3D.z);
        var pointer3DLength = Math.sqrt(pointer3D.x * pointer3D.x + pointer3D.z * pointer3D.z);

            //通过Math.atan2计算在XY面上与X轴的夹角弧度。
            //注：因为 z = -1，所以两者到原点的长度近似为x分量（数值的大小也不需要绝对对应）
        var anglePointerDragStart3DToX = Math.atan2(pointerDragStart3D.y, pointerDragStart3DLength);       //近似为 anglePointerDragStart3DToX = Math.atan2( pointerDragStart3D.y, pointerDragStart3D.x ) 
        var anglePointer3DToX = Math.atan2(pointer3D.y, pointer3DLength);                                 //近似为 anglePointer3DToX = Math.atan2( pointer3D.y, pointer3D.x )

        //算出两者角度差，作为竖直方向角度差值（rotationDifference.y）
        this.rotationDifference.y = THREE.Math.radToDeg(anglePointerDragStart3DToX - anglePointer3DToX);


        //y分量清零，原向量等价于在XZ轴上的投影向量
        pointerDragStart3D.y = 0;
        pointer3D.y = 0;

        //归一化（/length），求两者夹角作为
        //判断方向，最后记为水平方向角度差值（rotationDifference.x）
        var anglePointerDragStart3DToPointer3D = Math.acos(pointerDragStart3D.dot(pointer3D) / pointerDragStart3D.length() / pointer3D.length());

        if (!isNaN(anglePointerDragStart3DToPointer3D)) {
            this.rotationDifference.x = THREE.Math.radToDeg(anglePointerDragStart3DToPointer3D);
            if (this.pointerDragStart.x < this.pointer.x) {
                this.rotationDifference.x *= -1;
            }
        }

        //更新pointerDragStart记录当前帧坐标，用于下一帧求帧差值
        this.pointerDragStart.copy(this.pointer);
    }
}
PanoramaControls.prototype.onMouseMove = function(event) {
     
    this.updatePointer(event.clientX, event.clientY);

}
PanoramaControls.prototype.onTouchMove = function(event) {

    this.updatePointer(event.changedTouches[0].clientX, event.changedTouches[0].clientY)

}

PanoramaControls.prototype.updatePointer = function(screenX, screenY) {
    this.pointer.x = screenX / this.domElement.clientWidth * 2 - 1;   // 屏幕坐标换算相对于canvas的父级
    this.pointer.y = 2 * -(screenY / this.domElement.clientHeight) + 1;

}
PanoramaControls.prototype.endRotation = function() {


    this.pointerDragOn = false;
    try{
        var rotationHistoryAverage = averageVectors(this.rotationHistory);
    }catch(e){
        console.error(e)
    } 
    
    this.rotationSpeed.set(rotationHistoryAverage.x * 30, rotationHistoryAverage.y * 30);

}
PanoramaControls.prototype.onTouchEnd = function(event) {

    if (this.enabled) {

        event.preventDefault();
        event.stopPropagation();
        this.endRotation()
    }
}
PanoramaControls.prototype.onMouseUp = function(event) {


    if (this.enabled) {

        event.preventDefault();
        event.stopPropagation();
        this.endRotation()
    }
}
PanoramaControls.prototype.update = function(deltaTime) {

    if (this.enabled) {

        this.updateRotation();

        for (this.rotationHistory.push(this.rotationDifference.clone()); this.rotationHistory.length > 5;) {

            this.rotationHistory.shift();
        }

        this.lon += this.rotationDifference.x;
        this.lat += this.rotationDifference.y;
        this.rotationDifference.set(0, 0);
        this.rotationSpeed.x = this.rotationSpeed.x * (1 - 0.05) + this.rotationAcc.x * 4.5;
        this.rotationSpeed.y = this.rotationSpeed.y * (1 - 0.05) + this.rotationAcc.y * 4.5;

        this.lon += this.rotationSpeed.x * deltaTime;
        this.lat += this.rotationSpeed.y * deltaTime;

      

        this.lat = Math.max(this.latMin, Math.min(this.latMax, this.lat));
        this.phi = THREE.Math.degToRad(90 - this.lat);
        this.theta = THREE.Math.degToRad(this.lon);

        this.lookVector.x = Math.sin(this.phi) * Math.cos(this.theta);
        this.lookVector.y = Math.cos(this.phi);
        this.lookVector.z = Math.sin(this.phi) * Math.sin(this.theta);


        this.camera.position.add(this.translationWorldDelta) 
        this.translationWorldDelta.multiplyScalar(0.9); 
    
        this.target.copy(this.lookVector).add(this.aimFrom);
        this.camera.lookAt(this.target)
        
        
        
    }
}
PanoramaControls.prototype.onMouseWheel = function(event) {

    /* if (this.enabled) {

        // let z = void 0 !== event['wheelDelta'] ? event['wheelDelta'] : 0 !== event.detail && -event.detail;
        // this.flyDirection(new THREE.Vector3(0, 0, -z).normalize());
        this._wheel = Math.floor(event['wheelDeltaY'] / 120);
        this._wheel = Math.abs(this._wheel) > 0.1 ? Math.sign(this._wheel) : 0;
        if (this._wheel !== 0 && this.scrollZoomSta) {
            this._wheel > 0 ? this._wheel = 1 + this.scrollZoomSpeed : this._wheel = 1 - this.scrollZoomSpeed;
            let curZoomLevel = this._wheel * this.zoomLevel;
            this.zoomTo(curZoomLevel);
        }

    } */
    
    let delta
    if (event.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9
        delta = event.wheelDelta;
    } else if (event.detail !== undefined) { // Firefox
        delta = -event.detail;
    }
        
    
    
    if(delta != void 0){//滚轮缩放 
        if(delta == 0)return //mac
        let direction = new THREE.Vector3(0,0,-1).applyQuaternion(this.camera.quaternion)
        let moveSpeed = 0.1
        if(delta < 0) moveSpeed *=-1
        this.translationWorldDelta.add(direction.multiplyScalar(moveSpeed))
    }
    
}
PanoramaControls.prototype.zoomTo = function(curZoomLevel) {
    curZoomLevel < this.zoomMin && (curZoomLevel = this.zoomMin);
    curZoomLevel > this.zoomMax && (curZoomLevel = this.zoomMax);
    this.zoomLevel = curZoomLevel;
    this.camera.fov = this.baseFov*(1/this.zoomLevel);
    this.camera.updateProjectionMatrix();
} 

/*   
reset() {

    this.stop()
}

stop() {

    this.rotationAcc.set(0, 0);
    this.rotationSpeed.set(0, 0);
}
*/




//-------------copyFromPlayer
PanoramaControls.prototype.handleControlScroll = function(e) {
     e > 0 ? e = 1 + this.scrollZoomSpeed : e < 0 && (e = 1 - this.scrollZoomSpeed);
    0 !== e && this.zoomBy(e)
}
PanoramaControls.prototype.zoomBy = function(e) {
     this.zoomTo(this.zoomLevel * e);
}



function averageVectors(e, t) {
    var i = new THREE.Vector3();
    if (0 === e.length) return i;
    for (var r = 0, o = 0; o < e.length; o++) {
        var a = t ? e[o][t] : e[o];
        i.add(a), r++;
    }
    return i.divideScalar(r);
} 
 