123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719 |
- import math from './math.js'
- import * as THREE from 'three'
- var common = {
- valueFromHash(e, t) {
- var i = new RegExp('[#&?]' + e + '=([^#&?]*)'),
- n = i.exec(window.location.href)
- if (!n) return t
- var r = n[1]
- return 'boolean' == typeof t ? 'true' === r || '1' === r : 'number' == typeof t ? parseFloat(r) : window.decodeURIComponent(r)
- },
- lowerMedian(e, t) {
- if (0 === e.length) return null
- ;(t = t || 2),
- e.sort(function (e, t) {
- return e - t
- })
- var i = Math.floor(e.length / t)
- return e[i]
- },
- stableSort(e, t) {
- return e
- .map(function (e, t) {
- return {
- value: e,
- index: t,
- }
- })
- .sort(function (e, i) {
- var n = t(e.value, i.value)
- return 0 !== n ? n : e.index - i.index
- })
- .map(function (e) {
- return e.value
- })
- },
- sortByScore: function (list, request, rank) {
- var i = request ? common.filterAll(list, request) : list
- return 0 === i.length
- ? []
- : (i = i
- .map(function (e) {
- let scores = rank.map(function (f) {
- return f(e)
- }) //add
- return {
- item: e,
- scores,
- score: scores.reduce(function (t, i) {
- return t + i
- }, 0),
- }
- })
- .sort(function (e, t) {
- return t.score - e.score
- }))
- },
- filterAll(e, t) {
- return e.filter(function (e) {
- return t.every(function (t) {
- return t(e)
- })
- })
- },
- getMixedSet: function (arr1, arr2) {
- //交集
- return arr1.filter(item => arr2.includes(item))
- },
- getUnionSet: function (arr1, arr2) {
- //并集
- return arr1.concat(arr2.filter(item => !arr1.includes(item)))
- },
- getDifferenceSet: function (arr1, arr2) {
- //差集 不能识别重复的,如getDifferenceSet([1,2,2],[1,1,2]) 为空
- var arr11 = arr1.filter(item => !arr2.includes(item))
- var arr22 = arr2.filter(item => !arr1.includes(item))
- return arr11.concat(arr22)
- },
- getDifferenceSetMuti: function (arr) {
- //收集绝对没有重复的元素,也就是判断出现次数=1的
- var set = []
- arr.forEach(arr1 => {
- arr1.forEach(item => {
- var index = set.indexOf(item)
- if (index > -1) {
- set.splice(index, 1)
- } else {
- set.push(item)
- }
- })
- })
- return set
- },
- pushToGroupAuto: function (items, groups, recognizeFunction, judgeRelationFun) {
- //自动分组。 items是将分到一起的组合。items.length = 1 or 2.
- let isSame = (a, b) => {
- return a == b || (recognizeFunction && recognizeFunction(a, b))
- }
- var atGroups = groups.filter(group =>
- group.find(
- item => (isSame(item, items[0]) || isSame(item, items[1])) && (!judgeRelationFun || judgeRelationFun(group)) //根据关系进一步判断是否应该一组
- )
- )
- if (atGroups.length) {
- //在不同组
- //因为items是一组的,所以先都放入组1
- items.forEach(item => {
- if (!atGroups[0].includes(item)) atGroups[0].push(item)
- })
- if (atGroups.length > 1) {
- //如果在不同组,说明这两个组需要合并
- var combineGroup = []
- combineGroup.relationships = [items.slice()]
- atGroups.forEach(group => {
- let relationships = common.getUnionSet(combineGroup.relationships, group.relationships)
- combineGroup = common.getUnionSet(combineGroup, group)
- combineGroup.relationships = relationships
- groups.splice(groups.indexOf(group), 1)
- })
- groups.push(combineGroup)
- } else {
- atGroups[0].relationships.push(items.slice())
- }
- } else {
- //直接加入为一组
- items.relationships = [items.slice()]
- groups.push(items)
- }
- },
- disconnectGroup: function (pairs, groups, recognizeFunction) {
- //将atGroup中的pairs关系解除,然后重新分组
- let isSame = (a, b) => {
- return a == b || (recognizeFunction && recognizeFunction(a, b))
- }
- let oldGroups = groups.slice()
- pairs.forEach(items => {
- let relationship
- let atGroup = groups.find(group => {
- let r = group.relationships.find(arr => items.every(e => arr.some(a => isSame(a, e))))
- if (r) {
- relationship = r
- return true
- }
- }) //能找到relationships 有包含items的, 代表它们有绑定关系
- if (!atGroup) return
- //断开连接时,因为组内没有其他成员的连接信息,所以需要清除整组,并将剩余的一个个重新连接
- groups.splice(groups.indexOf(atGroup), 1) //删除
- atGroup.relationships.splice(atGroup.relationships.indexOf(relationship), 1)
- let newGroups_ = [] //为了防止裂解的该组(因之前有judgeRelationFun但现在没传)混入其他组,先放一个空组合里
- atGroup.relationships.forEach(pair => {
- //然后再重新生成这两个和组的关系,各自分组
- common.pushToGroupAuto(pair, newGroups_, recognizeFunction)
- })
- groups.push(...newGroups_)
- })
- let newGroups = groups.filter(e => !oldGroups.includes(e))
- return { newGroups }
- },
- removeFromGroup: function (items, atGroup, groups, recognizeFunction) {
- //将该组移除items中的所有元素,以及包含它的关系
- let isSame = (a, b) => {
- return a == b || (recognizeFunction && recognizeFunction(a, b))
- }
- let newRelations = atGroup.relationships.filter(arr => !arr.some(e => items.some(item => isSame(e, item))))
- if (newRelations.length == atGroup.relationships) return
- //断开连接时,因为组内没有其他成员的连接信息,所以需要清除整组,并将剩余的一个个重新连接
- groups.splice(groups.indexOf(atGroup), 1) //删除
- let newGroups = [] //为了防止裂解的该组(因之前有judgeRelationFun但现在没传)混入其他组,先放一个空组合里
- atGroup.relationships.forEach(pair => {
- //然后再重新生成这两个和组的关系,各自分组
- common.pushToGroupAuto(pair, newGroups, recognizeFunction)
- })
- groups.push(...newGroups)
- return { newGroups }
- },
- }
-
- common.dataURLtoBlob = function (dataurl) {
- //将base64转换blob
- var arr = dataurl.split(','),
- mime = arr[0].match(/:(.*?);/)[1],
- bstr = atob(arr[1]),
- n = bstr.length,
- u8arr = new Uint8Array(n)
- while (n--) {
- u8arr[n] = bstr.charCodeAt(n)
- }
- return new Blob([u8arr], { type: mime })
- }
- common.dataURLtoFile = function (dataurl, filename) {
- //将base64转换为文件
- var arr = dataurl.split(','),
- mime = arr[0].match(/:(.*?);/)[1],
- bstr = atob(arr[1]),
- n = bstr.length,
- u8arr = new Uint8Array(n)
- while (n--) {
- u8arr[n] = bstr.charCodeAt(n)
- }
- return new File([u8arr], filename, { type: mime })
- }
- common.saveFile = function (data, filename, cb) {
- var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a')
- save_link.href = data
- save_link.download = filename
- var event = document.createEvent('MouseEvents')
- event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
- save_link.dispatchEvent(event)
- cb && cb()
- }
-
- common.replaceAll = function (str, f, e) {
- //f全部替换成e
- var reg = new RegExp(f, 'g') //创建正则RegExp对象
- return str.replace(reg, e)
- }
- common.randomWord = function (randomFlag, min, max) {
- //随机字符串
- var str = '',
- range = min,
- arr = [
- '0',
- '1',
- '2',
- '3',
- '4',
- '5',
- '6',
- '7',
- '8',
- '9',
- 'a',
- 'b',
- 'c',
- 'd',
- 'e',
- 'f',
- 'g',
- 'h',
- 'i',
- 'j',
- 'k',
- 'l',
- 'm',
- 'n',
- 'o',
- 'p',
- 'q',
- 'r',
- 's',
- 't',
- 'u',
- 'v',
- 'w',
- 'x',
- 'y',
- 'z',
- 'A',
- 'B',
- 'C',
- 'D',
- 'E',
- 'F',
- 'G',
- 'H',
- 'I',
- 'J',
- 'K',
- 'L',
- 'M',
- 'N',
- 'O',
- 'P',
- 'Q',
- 'R',
- 'S',
- 'T',
- 'U',
- 'V',
- 'W',
- 'X',
- 'Y',
- 'Z',
- ]
- if (randomFlag) {
- // 随机长度
- range = Math.round(Math.random() * (max - min)) + min
- }
- for (var i = 0; i < range; i++) {
- var pos = Math.round(Math.random() * (arr.length - 1))
- str += arr[pos]
- }
- return str
- }
- common.getRandomSid = function () {
- //5-7位随机字符串 + 6位时间 为热点准备
- var pre = common.randomWord(true, 5, 7)
- var post = new Date().getTime() + ''
- var len = post.length
- post = post.substring(len - 8, len - 5) + post.substring(len - 3, len) //其实还是有可能重复的....
- return pre + post
- }
- common.getTime = function (second) {
- //秒
- var str = '' //不支持大于60分钟的时间哟
- var minute = parseInt(second / 60)
- if (minute < 10) str += '0'
- str += minute
- second = parseInt(second % 60) + ''
- if (second.length == 1) second = '0' + second
- str = str + ':' + second
- return str
- }
- ;(common.CloneJson = function (data) {
- var str = JSON.stringify(data)
- return JSON.parse(str)
- }),
- (common.CloneObject = function (copyObj, isSimpleCopy, simpleCopyList = [], judgeSimpleCopyFun) {
- //isSimpleCopy 只复制最外层
- //复制json result的可能:普通数字或字符串、普通数组、复杂对象
- simpleCopyList.includes(THREE.Object3D) || simpleCopyList.push(THREE.Object3D) //遇到simpleCopyList中的类直接使用不拷贝
- judgeSimpleCopyFun || (judgeSimpleCopyFun=()=>{})
-
- if (!copyObj || typeof copyObj == 'number' || typeof copyObj == 'string' || copyObj instanceof Function || simpleCopyList.some(className => copyObj instanceof className) || judgeSimpleCopyFun(copyObj)) {
- return copyObj
- }
- if (copyObj instanceof Array) {
- return copyObj.map(e => {
- return this.CloneObject(e, isSimpleCopy, simpleCopyList, judgeSimpleCopyFun)
- })
- } else {
- if (copyObj.clone instanceof Function) {
- //解决一部分
- return copyObj.clone()
- }
- }
- let result = {}
- for (var key in copyObj) {
- if (copyObj[key] instanceof Object && !isSimpleCopy ) result[key] = this.CloneObject(copyObj[key], isSimpleCopy, simpleCopyList, judgeSimpleCopyFun)
- else result[key] = copyObj[key]
- //如果是函数类同基本数据,即复制引用
- }
- return result
- }),
- (common.CloneClassObject = function (copyObj) {
- //复杂类对象
- var newobj = new copyObj.constructor()
- this.CopyClassObject(newobj, copyObj)
- return newobj
- }),
- (common.CopyClassObject = function (targetObj, copyObj) {
- //复杂类对象
- for (let i in copyObj) {
- if (i in copyObj.__proto__) break //到函数了跳出
- targetObj[i] = this.CloneObject(copyObj[i])
- }
- }),
- (common.ifSame = function (object1, object2) {
- if (object1 == object2) return true
- // 0 != undefined , 0 == ''
- else if (!object1 || !object2) return false
- else if (object1.constructor != object2.constructor) {
- return false
- } else if (object1 instanceof Array) {
- if (object1.length != object2.length) return false
- var _object2 = object2.slice(0)
- for (let i = 0; i < object1.length; i++) {
- var u = _object2.find(e => ifSame(object1[i], e))
- if (u == void 0 && !_object2.includes(u) && !object1.includes(u)) return false
- else {
- let index = _object2.indexOf(u)
- _object2.splice(index, 1)
- }
- }
- return true
- } else if (object1.equals instanceof Function) {
- //复杂数据仅支持这种,其他的可能卡住?
- return object1.equals(object2)
- } else if (typeof object1 == 'number' || typeof object1 == 'string') {
- if (isNaN(object1) && isNaN(object2)) return true
- else return object1 == object2
- } else if (typeof object1 == 'object') {
- var keys1 = Object.keys(object1)
- var keys2 = Object.keys(object2)
- if (!ifSame(keys1, keys2)) return false
- for (let i in object1) {
- var same = ifSame(object1[i], object2[i])
- if (!same) return false
- }
- return true
- } else {
- console.log('isSame出现例外')
- }
- })
-
-
-
-
-
-
-
-
-
- common.intervalTool = {
- //延时update,防止卡顿
- list: [],
- isWaiting: function (name, func, delayTime) {
- let item = this.list.find(e => e.name == name)
- if (!item) {
- //如果没有该项, 则加入循环
- let ifContinue = func()
- item = { name }
- this.list.push(item)
- setTimeout(() => {
- var a = this.list.indexOf(item)
- this.list.splice(a, 1)
- if (item.requestUpdate || ifContinue) this.isWaiting(name, func, delayTime) //循环
- }, delayTime)
- } else {
- //如果有该项,说明现在请求下一次继续更新
- /* if(delayTime == 0){//想立刻更新一次
- func()
- }else{ */
- item.requestUpdate = true
- //}
- }
- },
- }
- common.batchHandling = {
- //分批处理
- lists: [],
- getSlice: function (name, items, { stopWhenAllUsed, minCount = 5, maxCount = 100, durBound1, durBound2, maxUseCount }) {
- if (
- items.length == 0 ||
- ((maxUseCount = maxUseCount == void 0 ? common.getBestCount({ name, minCount, maxCount, durBound1, durBound2, ifLog: false }) : maxUseCount), !maxUseCount) //本次最多可以使用的个数
- ) {
- return { list: [] }
- }
- if (!this.lists[name]) this.lists[name] = { list: [] }
- //更新列表项目,但不变原来的顺序
- let list = this.lists[name].list.filter(a => items.some(item => a.item == item)) //去掉已经不在items里的项目
- this.lists[name].list = list
- items.forEach(item => {
- //增加新的项目。
- if (!list.some(a => a.item == item)) {
- list.push({ item, count: 0 })
- }
- })
- //至此,在后排的都是未使用的
- let unUsed = list.filter(e => e.count == 0) //未使用的项目(count为0)优先
- let result = []
- unUsed.slice(0, maxUseCount).forEach(e => {
- result.push(e.item)
- e.count++
- })
- if (unUsed.length > maxUseCount) {
- //还是剩有未使用的项目,等待下一次
- } else {
- //所有项目都能使用一次
- if (!stopWhenAllUsed) {
- //若不是全部使用就停止
- let wholeCount = Math.min(items.length, maxUseCount)
- let restCount = wholeCount - result.length //补齐
- list.slice(0, restCount).forEach(e => {
- result.push(e.item)
- e.count++
- })
- }
- list.forEach(e => e.count--) //复原,等待新的循环
- }
- return { list: result }
- },
- addSliceListen({ getList, callback, minCount, maxCount, durBound1, durBound2, maxHistory, player }) {
- let unUpdate, lastUpdate
- player.on('update', e => {
- if (player.flying) return
- let waitForUpdate = getList()
- let stopWhenAllUsed = !player.lastFrameChanged
- let standardUpdate = player.lastFrameChanged || !lastUpdate //相机变化或第一次
- let list
- if (standardUpdate) {
- list = waitForUpdate
- unUpdate = null
- } else {
- if (!unUpdate) {
- unUpdate = common.getDifferenceSet(waitForUpdate, lastUpdate)
- //unUpdate = unUpdate.filter(e => e.visible) //如飞出后最后一次更新之后,都隐藏了,隐藏的就不用更新了
- }
- list = unUpdate
- }
- let result = common.batchHandling.getSlice('ifVideoInsight', list, { stopWhenAllUsed, minCount, maxCount, durBound1: 3, durBound2: 13, maxHistory: 3 }) //iphonex稳定后大概在7-10。
- let updateList = result.list
- //updateList.length && console.log(updateList.map(e=>e.sid))
- updateList.forEach(callback)
- if (!standardUpdate) {
- //相机停止变化后只更新还未更新的
- unUpdate = common.getDifferenceSet(unUpdate, updateList)
- }
- lastUpdate = updateList
- })
- },
- }
- common.getBestCount = (function () {
- let lastCount = {}
- return function ({ name, minCount = 1, maxCount = 6, durBound1 = 1, durBound2 = 4, ifLog, maxHistory }) {
- let timeStamp = performance.getEntriesByName('loop-start')
- let count
- if (timeStamp.length) {
- let dur = performance.now() - timeStamp[timeStamp.length - 1].startTime
- /*let k = -(maxCount - minCount) / (durBound2 - durBound1)
- let m = maxCount - durBound1 * k
- count = THREE.MathUtils.clamp(Math.round(k * dur + m), minCount, maxCount) //dur在iphoneX中静止有7,pc是2
- */
- count = Math.round(math.linearClamp(dur, durBound1, durBound2, maxCount, minCount))
- if (maxHistory) {
- if (!lastCount[name]) lastCount[name] = []
- if (count == 0 && lastCount[name].length > maxHistory - 1 && !lastCount[name].some(e => e > 0)) {
- count = 1
- }
- lastCount[name].push(count)
- if (lastCount[name].length > maxHistory) lastCount[name].splice(0, 1)
- }
- ifLog && console.log(name, count, ' ,dur:', dur.toFixed(3))
- } else {
- count = maxCount // ?
- }
- //主要在手机端有效果。
- return count
- }
- })()
- common.timeMeasuring = {
- reportTimings: false,
- collection: {},
- registerCollect(name, o) {
- this.collection[name] = o
- o.measures = []
- o.sum = 0
- },
- addTimeMark: function (name, type, ifLog) {
- let record = this.collection[name]
- let now = performance.now()
- let needRecord = record && (record.measures.length < record.minCount || now - record.lastAddTime > record.refreshTime) //间隔时间超过refreshTime重新收集
- if (needRecord || this.reportTimings) {
- if (type == 'end' && performance.getEntriesByName(name + '-start').length == 0) return
- performance.mark(name + '-' + type)
- if (type == 'end') {
- let measure = performance.measure(name, name + '-start', name + '-end')
- if (!measure) {
- //console.error('没找到measure',name) //可能是其他地方报错了没进行下去所以找不到
- return
- }
- if (ifLog) console.log(name, '耗时', measure.duration.toFixed(3))
- if (needRecord) {
- if (record.measures.length >= record.minCount) {
- //先清空上一轮的
- record.measures = []
- record.sum = 0
- }
- record.measures.push(measure.duration)
- record.sum += measure.duration
- record.mean = record.sum / record.measures.length
- record.measures.sort((a, b) => a - b)
- record.median = record.measures[parseInt(record.measures.length / 2)]
- record.lastAddTime = now
- if (record.measures.length == record.minCount) {
- //console.log(record)
- }
- }
- }
- }
- },
- report: function (timestamp) {
- //原resolveTimings
- //打印用时。 注:performance手机的精度只到整数位
- if (!this.toggle) {
- this.toggle = timestamp
- }
- let duration = timestamp - this.toggle
- if (duration > 1000.0) {
- if (this.reportTimings) {
- let measures = performance.getEntriesByType('measure')
- let names = new Set()
- for (let measure of measures) {
- names.add(measure.name)
- }
- let groups = new Map()
- for (let name of names) {
- groups.set(name, {
- measures: [],
- sum: 0,
- n: 0,
- min: Infinity,
- max: -Infinity,
- })
- }
- for (let measure of measures) {
- let group = groups.get(measure.name)
- group.measures.push(measure)
- group.sum += measure.duration
- group.n++
- group.min = Math.min(group.min, measure.duration)
- group.max = Math.max(group.max, measure.duration)
- }
- for (let [name, group] of groups) {
- group.mean = group.sum / group.n
- group.measures.sort((a, b) => a.duration - b.duration)
- if (group.n === 1) {
- group.median = group.measures[0].duration
- } else if (group.n > 1) {
- group.median = group.measures[parseInt(group.n / 2)].duration
- }
- }
- let cn = Array.from(names).reduce((a, i) => Math.max(a, i.length), 0) + 5
- let cmin = 5
- let cmed = 5
- let cmax = 5
- let csam = 4
- let message =
- ` ${'NAME'.padEnd(cn)} |` +
- ` ${'MIN'.padStart(cmin)} |` +
- ` ${'MEDIAN'.padStart(cmed)} |` +
- ` ${'MAX'.padStart(cmax)} |` +
- ` ${'AVE'.padStart(cmax)} |` +
- ` ${'SAMPLES'.padStart(csam)} \n`
- message += ` ${'-'.repeat(message.length)}\n`
- names = Array.from(names).sort()
- for (let name of names) {
- let group = groups.get(name)
- let min = group.min.toFixed(2)
- let median = group.median.toFixed(2)
- let max = group.max.toFixed(2)
- let n = group.n
- let ave = group.mean.toFixed(2) //add
- message +=
- ` ${name.padEnd(cn)} |` +
- ` ${min.padStart(cmin)} |` +
- ` ${median.padStart(cmed)} |` +
- ` ${max.padStart(cmax)} |` +
- ` ${ave.padStart(cmax)} |` +
- ` ${n.toString().padStart(csam)}\n`
- }
- message += `\n`
- console.log(message)
- }
- performance.clearMarks()
- performance.clearMeasures()
- this.toggle = timestamp
- }
- },
- }
-
- export default common
|