import * as THREE from "../../../libs/three.js/build/three.module.js"; import math from './math.js' //THREE.Vector2.name2 = 'Common' var Common = { sortByScore: function(list, request, rank){ var i = request ? Common.filterAll(list, request) : list return 0 === i.length ? [] : i = i.map(function(e) { let results = rank.map(function(f){return f(e)}) let scores = results.map(e=>e.score != void 0 ? e.score : e) let logs = results.map(e=>e.log) return { item: e, scores, logs, score: scores.reduce(function(t, i) {//总分 return t + i }, 0) } }).sort(function(e, t) { return t.score - e.score; }) } , filterAll: function(e, t) { return e.filter(function (e) { return t.every(function (t) { return t(e) }) }) }, //--------------- find : function(list, request, rank, sortByScore ) { if(sortByScore){ var r = this.sortByScore(list, request, rank) return r[0] && r[0].item }else{ var i = request ? Common.filterAll(list, request) : list return 0 === i.length ? null : (rank && rank.forEach(function(e) { i = Common.stableSort(i, e) }), i[0]) } } , stableSort: function(e, f) {//用到排序函数,涉及到两个item相减 return e.map(function(e, i) { return { value: e, index: i } }).sort(function(e, u) { var n = f(e.value, u.value); return 0 !== n ? n : e.index - u.index //似乎就是加多了这一步:若差距为0,按照原顺序 }).map(function(e) { return e.value }) }, average: function (e, t) { if (0 === e.length) return null; for (var i = 0, n = 0, r = 0; r < e.length; r++) { var o = t ? e[r][t] : e[r]; i += o, n++ } return i / n }, //--------------------------- 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; } , CloneJson : function(data){ var str = JSON.stringify(data) return JSON.parse(str) } , CloneObject : function (copyObj, isSimpleCopy, simpleCopyList = [], judgeSimpleCopyFun) { //isSimpleCopy 只复制最外层 //复制json result的可能:普通数字或字符串、普通数组、复杂对象 judgeSimpleCopyFun || (judgeSimpleCopyFun=()=>{}) if (!copyObj || typeof copyObj == 'number' || typeof copyObj == 'string' ||copyObj.isObject3D || 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 } , CloneClassObject :function(copyObj, {ignoreList=[],simpleCopyList=[]}={}){//复杂类对象 var newobj = new copyObj.constructor(); this.CopyClassObject(newobj, copyObj, {ignoreList,simpleCopyList}) return newobj } , CopyClassObject :function(targetObj, copyObj, {ignoreList=[],simpleCopyList=[]}={}){//复杂类对象 for(let i in copyObj){ if(i in copyObj.__proto__)break; //到函数了跳出 if(ignoreList.includes(i)){ continue; }else if(simpleCopyList.includes(i)){ targetObj[i] = copyObj[i] }else{ targetObj[i] = this.CloneObject(copyObj[i], false, simpleCopyList ) } /* else if(copyObj[i].clone instanceof Function ){ targetObj[i] = copyObj[i].clone() }else{ targetObj[i] = copyObj[i]; } */ } } , ifSame : function(object1, object2, simpleEqualClass=[]){ //对于复杂的类对象,若能简单判断就直接写进simpleEqualClass if(object1 == object2 )return true // 0 != undefined , 0 == '' else if(!object1 || !object2) return false else if(object1.constructor != object2.constructor){ return false }else if(simpleEqualClass.some(className => object1 instanceof className)){ return object1 == object2 }else if(object1 instanceof Array ) { if(object1.length != object2.length)return false; var _object2 = object2.slice(0); for(let i=0;iCommon.ifSame(object1[i], e, simpleEqualClass)); 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(!Common.ifSame(keys1,keys2,simpleEqualClass))return false; for(let i in object1){ var same = Common.ifSame(object1[i], object2[i],simpleEqualClass); if(!same)return false } return true }else{ console.log('isSame出现例外') } } , downloadFile : 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(); }, replaceAll : function (str, f, e) { //f全部替换成e var reg = new RegExp(f, "g"); //创建正则RegExp对象 return str.replace(reg, e); }, dealURL(url){ let urlNew = this.replaceAll(url, "\\+", "%2B");// 浏览器似乎不支持访问带+的地址 urlNew = this.replaceAll(urlNew, "/.//", "/") //去除双斜杠(/.//) return urlNew }, getNameFromURL(url){ if(!url)return '' let get = (e)=>{ return e.split('/').pop() } if(url instanceof Array){ return url.map(e=>get(e)) } return get(url) }, //--------------------------- intervalTool:{ //延时update,防止卡顿 list:[], /* isWaiting:function(name, func, delayTime){ if(!this.list.includes(name)){ //如果没有该项, 则开始判断 var needWait = func(); //触发了改变,则等待一段时间后再自动判断 if(needWait){ this.list.push(name); setTimeout(()=>{ var a = this.list.indexOf(name); this.list.splice(a,1); this.isWaiting(name, func, delayTime) //循环 },delayTime) } } }, */ isWaiting:function(name, func, delayTime/* , autoCycle */){ let item = this.list.find(e=>e.name == name) if(!item){ //如果没有该项, 则加入循环 let ifContinue = func() item = {name, func, delayTime} this.list.push(item); setTimeout(()=>{ var a = this.list.indexOf(item); this.list.splice(a,1); let {func, delayTime} = item if(item.requestUpdate || ifContinue ) this.isWaiting(name, func, delayTime) //循环 },delayTime) }else{//如果有该项,说明现在请求下一次继续更新 //if(delayTime == 0){//想立刻更新一次 // func() //}else{ //更新属性 item.func = func item.delayTime = delayTime item.requestUpdate = true //} } }, } , pushToGroupAuto : function(items, groups, recognizeFunction, recognizeGroup){//自动分组。 items是将分到一起的组合。items.length = 1 or 2. recognizeFunction = recognizeFunction || function(){} if(recognizeGroup){ //有更复杂的识别处理,直接传递整个组 var atGroups = groups.filter(group=>recognizeGroup(group, items)) }else{ var atGroups = groups.filter(group=>group.find( item => items[0] == item || recognizeFunction(item, items[0]) || items[1] == item || items[1] && recognizeFunction(item, items[1]) )) } if(atGroups.length){//在不同组 //因为items是一组的,所以先都放入组1 items.forEach(item=> {if(!atGroups[0].includes(item)) atGroups[0].push(item);}) if(atGroups.length>1){//如果在不同组,说明这两个组需要合并 var combineGroup = [] atGroups.forEach(group=>{ combineGroup = Common.getUnionSet(combineGroup, group) groups.splice(groups.indexOf(group),1) }) groups.push(combineGroup) } }else{//直接加入为一组 groups.push(items) } }, getBestCount : (function(){ let lastCount = {} return function(name, minCount=1,maxCount=6, durBound1 = 1.2, durBound2 = 10, ifLog, maxHistory){ let timeStamp = performance.getEntriesByName("loop-start"); let count if(timeStamp.length){ let dur = performance.now() - timeStamp[timeStamp.length-1].startTime; //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) } if(ifLog){//注意,console.log本身用时挺高, 降4倍时可能占用0.5毫秒 name && count && console.log(name, count , ' ,dur:', dur.toFixed(3)) } }else{ count = maxCount // ? } //主要在手机端有效果。 return count } })(), batchHandling : {//分批处理 lists:[], getSlice : function(name, items , {stopWhenAllUsed, min=5,max=100, durBound1 , durBound2, useEquals , maxUseCount}){ if(items.length == 0 || ((maxUseCount = maxUseCount == void 0 ? Common.getBestCount(name, min,max , durBound1, durBound2 /* , true */ ) : maxUseCount), !maxUseCount) //本次最多可以使用的个数 ){ return {list:[]} } if(!this.lists[name]) this.lists[name] = {list:[] } //更新列表项目,但不变原来的顺序 let list = this.lists[name].list.filter(a=>items.some(item=> useEquals ? a.item.equals(item) : a.item == item))//去掉已经不在items里的项目 this.lists[name].list = list items.forEach(item=>{//增加新的项目。 if(!list.some(a=>useEquals ? a.item.equals(item) : 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--) //复原,等待新的循环 } /* result.forEach((e,i)=>{//有重复的 if( result.slice(0,i).some(a=>a.equals(e)) || result.slice(i+1).some(a=>a.equals(e)) ) { console.log(e) } }) */ return {list:result } } }, getRootWindow(){//获取包含Potree的根window let win = window try{ while(win.parent!=win && win.parent.Potree){ win = win.parent } if(window != win)return win }catch(e){ //console.log(e) //可能跨域,从而win.parent.Potree报错 console.log('getRootWindow 跨域') return } }, watch: function(object, propName, initialValue){ //监听某个属性的变化 let v = initialValue Object.defineProperty(object, propName, { get: function() { return v }, set: function(e) { console.warn('watch:',propName, e) v = e } }) }, } Potree.Common = Common export default Common