var lerp = {
    vector: function (e, t, f) {//xzw change, add f
        var i = e.clone();
        return t = t.clone(),
            function (n) {
                e.set(i.x * (1 - n) + t.x * n, i.y * (1 - n) + t.y * n, i.z * (1 - n) + t.z * n)
                f && f(e, n);
            }
    },
    quaternion: function (e, t, f) {//xzw change, add f
        var i = e.clone();
        return function (n) {
            e.copy(i).slerp(t, n);
            f && f(e, n);
        }
    },
    property: function (e, t, i, n) {
        var r = e[t];
        return function (o) {
            e[t] = r * (1 - o) + i * o,
                n && n(e[t])
        }
    },
    uniform: function (e, t, i) {
        var n = e.material.uniforms[t].value;
        return function (r) {
            try {
                e.material.uniforms[t] && (e.material.uniforms[t].value = n * (1 - r) + i * r)
            } catch (e) {
                console.log(1)
            }

        }
    },
    matrix4: function (e, t) {
        var i = e.clone();
        return function (n) {
            for (var r = e.elements, o = i.elements, a = t.elements, s = 0; s < 16; s++)
                r[s] = o[s] * (1 - n) + a[s] * n
        }
    },
    allUniforms: function (e, t, i) {
        var n = e.map(function (e) {
            return this.uniform(e, t, i)
        }
            .bind(this));
        return function (e) {
            n.forEach(function (t) {
                t(e)
            })
        }
    }
};


//////

var easing = {};
//渐变曲线函数，反应加速度的变化
easing.linearTween = function (e, t, i, n) {
    return i * e / n + t
}
    ,
    easing.easeInQuad = function (e, t, i, n) {
        return e /= n,
            i * e * e + t
    }
    ,
    easing.easeOutQuad = function (e, t, i, n) {
        return e /= n,
            -i * e * (e - 2) + t
    }
    ,
    easing.easeInOutQuad = function (e, t, i, n) {
        return e /= n / 2,
            e < 1 ? i / 2 * e * e + t : (e--,
                -i / 2 * (e * (e - 2) - 1) + t)
    }
    ,
    easing.easeInCubic = function (e, t, i, n) {
        return e /= n,
            i * e * e * e + t
    }
    ,
    easing.easeOutCubic = function (e, t, i, n) {
        return e /= n,
            e--,
            i * (e * e * e + 1) + t
    }
    ,
    easing.easeInOutCubic = function (e, t, i, n) {
        return e /= n / 2,
            e < 1 ? i / 2 * e * e * e + t : (e -= 2,
                i / 2 * (e * e * e + 2) + t)
    }
    ,
    easing.easeInQuart = function (e, t, i, n) {
        return e /= n,
            i * e * e * e * e + t
    }
    ,
    easing.easeOutQuart = function (e, t, i, n) {
        return e /= n,
            e--,
            -i * (e * e * e * e - 1) + t
    }
    ,
    easing.easeInOutQuart = function (e, t, i, n) {
        return e /= n / 2,
            e < 1 ? i / 2 * e * e * e * e + t : (e -= 2,
                -i / 2 * (e * e * e * e - 2) + t)
    }
    ,
    easing.easeInQuint = function (e, t, i, n) {
        return e /= n,
            i * e * e * e * e * e + t
    }
    ,
    easing.easeOutQuint = function (e, t, i, n) {
        return e /= n,
            e--,
            i * (e * e * e * e * e + 1) + t
    }
    ,
    easing.easeInOutQuint = function (e, t, i, n) {
        return e /= n / 2,
            e < 1 ? i / 2 * e * e * e * e * e + t : (e -= 2,
                i / 2 * (e * e * e * e * e + 2) + t)
    }
    ,
    easing.easeInSine = function (e, t, i, n) {
        return -i * Math.cos(e / n * (Math.PI / 2)) + i + t
    }
    ,
    easing.easeOutSine = function (e, t, i, n) {
        return i * Math.sin(e / n * (Math.PI / 2)) + t
    }
    ,
    easing.easeInOutSine = function (e, t, i, n) {
        return -i / 2 * (Math.cos(Math.PI * e / n) - 1) + t
    }
    ,
    easing.easeInExpo = function (e, t, i, n) {
        return i * Math.pow(2, 10 * (e / n - 1)) + t
    }
    ,
    easing.easeOutExpo = function (e, t, i, n) {
        return i * (-Math.pow(2, -10 * e / n) + 1) + t
    }
    ,
    easing.easeInOutExpo = function (e, t, i, n) {
        return e /= n / 2,
            e < 1 ? i / 2 * Math.pow(2, 10 * (e - 1)) + t : (e--,
                i / 2 * (-Math.pow(2, -10 * e) + 2) + t)
    }
    ,
    easing.easeInCirc = function (e, t, i, n) {
        return e /= n,
            -i * (Math.sqrt(1 - e * e) - 1) + t
    }
    ,
    easing.easeOutCirc = function (e, t, i, n) {
        return e /= n,
            e--,
            i * Math.sqrt(1 - e * e) + t
    }
    ,
    easing.easeInOutCirc = function (e, t, i, n) {
        return e /= n / 2,
            e < 1 ? -i / 2 * (Math.sqrt(1 - e * e) - 1) + t : (e -= 2,
                i / 2 * (Math.sqrt(1 - e * e) + 1) + t)
    }
    ,
    easing.easeInElastic = function (e, t, i, n) {
        var r = 1.70158
            , o = 0
            , a = i;
        return 0 === e ? t : 1 === (e /= n) ? t + i : (o || (o = .3 * n),
            a < Math.abs(i) ? (a = i,
                r = o / 4) : r = o / (2 * Math.PI) * Math.asin(i / a),
            -(a * Math.pow(2, 10 * (e -= 1)) * Math.sin((e * n - r) * (2 * Math.PI) / o)) + t)
    }
    ,
    easing.easeOutElastic = function (e, t, i, n) {
        var r = 1.70158
            , o = 0
            , a = i;
        return 0 === e ? t : 1 === (e /= n) ? t + i : (o || (o = .3 * n),
            a < Math.abs(i) ? (a = i,
                r = o / 4) : r = o / (2 * Math.PI) * Math.asin(i / a),
            a * Math.pow(2, -10 * e) * Math.sin((e * n - r) * (2 * Math.PI) / o) + i + t)
    }
    ,
    easing.easeInOutElastic = function (e, t, i, n) {
        var r = 1.70158
            , o = 0
            , a = i;
        return 0 === e ? t : 2 === (e /= n / 2) ? t + i : (o || (o = n * (.3 * 1.5)),
            a < Math.abs(i) ? (a = i,
                r = o / 4) : r = o / (2 * Math.PI) * Math.asin(i / a),
            e < 1 ? -.5 * (a * Math.pow(2, 10 * (e -= 1)) * Math.sin((e * n - r) * (2 * Math.PI) / o)) + t : a * Math.pow(2, -10 * (e -= 1)) * Math.sin((e * n - r) * (2 * Math.PI) / o) * .5 + i + t)
    }
    ,
    easing.easeInBack = function (e, t, i, n, r) {
        return void 0 === r && (r = 1.70158),
            i * (e /= n) * e * ((r + 1) * e - r) + t
    }
    ,
    easing.easeOutBack = function (e, t, i, n, r) {
        return void 0 === r && (r = 1.70158),
            i * ((e = e / n - 1) * e * ((r + 1) * e + r) + 1) + t
    }
    ,
    easing.easeInOutBack = function (e, t, i, n, r) {
        return void 0 === r && (r = 1.70158),
            (e /= n / 2) < 1 ? i / 2 * (e * e * (((r *= 1.525) + 1) * e - r)) + t : i / 2 * ((e -= 2) * e * (((r *= 1.525) + 1) * e + r) + 2) + t
    }
    ,
    easing.easeOutBounce = function (e, t, i, n) {
        return (e /= n) < 1 / 2.75 ? i * (7.5625 * e * e) + t : e < 2 / 2.75 ? i * (7.5625 * (e -= 1.5 / 2.75) * e + .75) + t : e < 2.5 / 2.75 ? i * (7.5625 * (e -= 2.25 / 2.75) * e + .9375) + t : i * (7.5625 * (e -= 2.625 / 2.75) * e + .984375) + t
    }
    ,
    easing.easeInBounce = function (e, t, i, r) {
        return i - easing.easeOutBounce(r - e, 0, i, r) + t
    }
    ,
    easing.easeInOutBounce = function (e, t, i, r) {
        return e < r / 2 ? .5 * easing.easeInBounce(2 * e, 0, i, r) + t : .5 * easing.easeOutBounce(x, 2 * e - r, 0, i, r) + .5 * i + t
    }





/* 
    渐变
    

 */

var transitions = {
    globalDone: null,
    funcs: [],
    counter: 0,
    uniqueID: 0,
    start: function (e, t, i, r, o, a, s, cancelFun) {
        return r = r || 0,
            this.funcs.push({
                func: e,
                current: -r * Math.abs(t),                      //当前时间
                duration: (1 - Math.max(r, 0)) * Math.abs(t),   //总时长
                done: i,
                easing: o || easing.linearTween,                //渐变曲线
                cycling: t < 0,
                running: !0,
                debug: r < 0,
                name: a || "T" + this.counter,
                id: void 0 === s ? this.counter : s,
                paused: !1,
                cancelFun: cancelFun,   //取消时执行的函数
            }),
            e(0, 16),
            this.counter += 1,
            e
    },
    trigger: function (e) {
        var t = void 0 === e.delayRatio ? 0 : e.delayRatio
            , i = e.func || function () { }
            , r = void 0 === e.duration ? 0 : e.duration;
        void 0 !== e.cycling && e.cycling && (r = -Math.abs(r));
        var o = e.done || null
            , a = e.easing || easing.linearTween
            , s = e.name || "R" + this.counter
            , l = void 0 === e.id ? this.counter : e.id;
        return this.start(i, r, o, t, a, s, l)
    },
    setTimeout: function (e, t, i) {
        var n = void 0 === i ? this.counter : i;
        return this.trigger({
            done: e,
            duration: void 0 === t ? 0 : t,
            name: "O" + this.counter,
            id: n
        })
    },
    pause: function () {
        this.paused = !0
    },
    resume: function () {
        this.paused = !1
    },
    update: function (e) {
        this.funcs.forEach(function (t) {
            if (!(t.paused || (t.current += 1e3 * e,
                t.current < 0)))
                if (t.current >= t.duration && !t.cycling) {
                    var i = t.easing(1, 0, 1, 1);
                    t.func(i, 1e3 * e),
                        t.done && t.done(),
                        t.running = !1
                } else {
                    var n = t.easing(t.current % t.duration / t.duration, 0, 1, 1)
                        , r = t.func(n, 1e3 * e) || !1;
                    r && (t.done && t.done(),
                        t.running = !1)
                }
        });
        var t = this.funcs.length;
        this.funcs = this.funcs.filter(function (e) {
            return e.running
        });
        var i = this.funcs.length;
        if (t > 0 && 0 === i && this.globalDone) {
            var n = this.globalDone;
            this.globalDone = null,
                n()
        }
    },
    adjustSpeed: function (e, t) {
        for (var i = this.getById(e), n = 0; n < i.length; n++) {
            var r = i[n];
            r.duration /= t,
                r.current /= t
        }
    },
    getById: function (e) {
        return this.funcs.filter(function (t) {
            return e === t.id
        })
    },
    get: function (e) {
        for (var t = 0; t < this.funcs.length; t += 1)
            if (this.funcs[t].func === e)
                return this.funcs[t];
        return null
    },
    isRunning: function (e) {
        var t = this.get(e);
        return null !== t && t.running
    },
    countActive: function () {
        for (var e = 0, t = 0; t < this.funcs.length; t += 1)
            e += this.funcs[t].running;
        return e
    },
    listActive: function () {
        for (var e = [], t = 0; t < this.funcs.length; t += 1)
            this.funcs[t].running && e.push(this.funcs[t].name);
        return e
    },
    done: function (e) {
        this.globalDone = e
    },
    cancelById: function (e, dealCancelFun) { //xzw add dealDone
        var t = void 0 === e ? 0 : e;

        this.funcs = this.funcs.filter(function (e) {
            var is = e.id == t;

            if (is && dealCancelFun) {
                e.cancelFun && e.cancelFun()
            }
            return !is
        })
    },
    cancel: function (e) {
        this.funcs = this.funcs.filter(function (t) {
            return t.func !== e
        })
    },
    getUniqueId: function () {
        return this.uniqueID -= 1,
            this.uniqueID
    }
};


let convertTool = {

    getPos2d: function (point, camera, dom) {//获取一个三维坐标对应屏幕中的二维坐标



        if (!camera) return
        var pos = point.clone().project(camera)	//比之前hotspot的计算方式写得简单  project用于3转2(求法同shader)； unproject用于2转3 :new r.Vector3(e.x, e.y, -1).unproject(this.camera);

        var x, y;
        x = (pos.x + 1) / 2 * dom.clientWidth;
        y = (1 - (pos.y + 1) / 2) * dom.clientHeight;

        var inSight = x <= dom.clientWidth && x >= 0    //是否在屏幕中   
            && y <= dom.clientHeight && y >= 0


        return {
            pos: new THREE.Vector2(x, y),  // 屏幕像素坐标
            vector: pos,   //（范围 -1 ~ 1）
            trueSide: pos.z < 1, //trueSide为false时，即使在屏幕范围内可见，也是反方向的另一个不可以被渲染的点   参见Tag.update
            inSight: inSight	//在屏幕范围内可见
        };
    },

    ifShelter: function (pos3d, pos2d, camera, colliders, margin = 0) {
        //检测某点在视线中是否被mesh遮挡
        if (!pos2d) pos2d = convertTool.getPos2d(pos3d)
        camera = camera || player.camera
        var ori = new THREE.Vector3(pos2d.x, pos2d.y, -1).unproject(camera) //找到视线原点
        var dir = pos3d.clone().sub(ori).normalize()
        var ray = new THREE.Raycaster(ori, dir); //由外向里 因为模型从内侧是可见的所以从外侧
        var o = ray.intersectObjects(colliders);

        var len = pos3d.distanceTo(ori);
        if (o && o.length) {
            for (var i = 0; i < o.length; i++) {
                if (o[i].distance < len - margin) { return true; }//有遮挡
            }
        }
    },

    updateVisible: function (object, reason, ifShow, level = 0, type) {//当所有加入的条件都不为false时才显示. reason='force'一般是强制、临时的
        if (!object.unvisibleReasons) object.unvisibleReasons = []; //如果length>0代表不可见
        if (!object.visibleReasons) object.visibleReasons = []; //在同级时，优先可见


        var update = function () {

            //先按从高到低的level排列
            object.unvisibleReasons = object.unvisibleReasons.sort((a, b) => b.level - a.level)
            object.visibleReasons = object.visibleReasons.sort((a, b) => b.level - a.level)
            var maxVisiLevel = object.visibleReasons[0] ? object.visibleReasons[0].level : -1
            var maxunVisiLevel = object.unvisibleReasons[0] ? object.unvisibleReasons[0].level : -1

            var shouldVisi = maxVisiLevel >= maxunVisiLevel
            var visiBefore = object.visible


            if (visiBefore != shouldVisi) {
                object.visible = shouldVisi
                object.dispatchEvent({
                    type: 'isVisible',
                    visible: shouldVisi,
                    reason,
                })
            }


        }



        if (ifShow) {

            var index = object.unvisibleReasons.findIndex(e => e.reason == reason)
            if (index > -1) {
                type = 'cancel'
                object.unvisibleReasons.splice(index, 1);
            }

            if (type == 'add') {
                if (!object.visibleReasons.some(e => e.reason == reason)) {
                    object.visibleReasons.push({ reason, level })
                }
            }
        } else {
            var index = object.visibleReasons.findIndex(e => e.reason == reason)
            if (index > -1) {
                type = 'cancel'
                object.visibleReasons.splice(index, 1);
            }

            if (type != 'cancel') {
                if (!object.unvisibleReasons.some(e => e.reason == reason)) {
                    object.unvisibleReasons.push({ reason, level })
                }
            }
        }

        update()

    },


    toPrecision: function (e, t) {//xzw change 保留小数
        var f = function (e, t) {
            var i = Math.pow(10, t);
            return Math.round(e * i) / i
        }
        if (e instanceof Array) {
            for (var s = 0; s < e.length; s++) {
                e[s] = f(e[s], t);
            }
            return e;
        } else if (e instanceof Object) {
            for (var s in e) {
                e[s] = f(e[s], t);
            }
            return e;
        } else return f(e, t)
    },



}



let math = {
    closeTo : function(a,b, precision=1e-6){ 
        let f = (a,b)=>{
            return Math.abs(a-b) < precision;
        }; 
          
        if(typeof (a) == 'number'){
            return f(a, b);
        }else {
            let judge = (name)=>{
                if(a[name] == void 0)return true //有值就判断，没值就不判断
                else return f(a[name],b[name])
            };
            return judge('x') && judge('y') && judge('z') && judge('w')  
        } 
        
    }, 


}

