|
@@ -0,0 +1,829 @@
|
|
|
+
|
|
|
+/*
|
|
|
+import {Utils} from "../../../utils.js";
|
|
|
+import Sprite from '../../objects/Sprite.js'
|
|
|
+
|
|
|
+import browser from '../../utils/browser.js'
|
|
|
+ */
|
|
|
+import Common from "../../utils/Common.js";
|
|
|
+import {ExtendView} from "../../../viewer/ExtendView.js";
|
|
|
+import * as THREE from "../../../../libs/three.js/build/three.module.js";
|
|
|
+import Viewport from "../../viewer/Viewport.js";
|
|
|
+import {ExtendPointCloudMaterial} from "../../../materials/ExtendPointCloudMaterial.js";
|
|
|
+import math from "../../utils/math.js";
|
|
|
+//import {Prism} from './Prism.js'
|
|
|
+
|
|
|
+const maxWidth = 4096
|
|
|
+const surfaceThick = 0.1 ;//最大表面厚度
|
|
|
+const pointDensity = 'screenshot'
|
|
|
+const maxPointBudge = Potree.config.pointDensity[pointDensity].pointBudget
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+let horizonZ, zMin, zMax, lowest, highest, wDelta, sDelta, deferred, interrupt, timestamp
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+let testPoint = false
|
|
|
+
|
|
|
+
|
|
|
+//const asyncTimeout = (delay) => new Promise(resolve => setTimeout(resolve, delay))
|
|
|
+const delayForNotify = 5, notifyInterval = 500
|
|
|
+
|
|
|
+
|
|
|
+let lastNotifyTime = Date.now()
|
|
|
+let curPercent
|
|
|
+const notify = (areas, num, str )=>{ //num: 0-1
|
|
|
+ return new Promise(resolve => {
|
|
|
+ if(!deferred)return resolve()
|
|
|
+
|
|
|
+ let now = Date.now()
|
|
|
+ if(now - lastNotifyTime < notifyInterval){
|
|
|
+ return resolve()
|
|
|
+ }
|
|
|
+ lastNotifyTime = now
|
|
|
+ if(num == void 0){
|
|
|
+ num = curPercent
|
|
|
+ }else{
|
|
|
+ let s = 0, e = 1
|
|
|
+ if(num instanceof Function ){
|
|
|
+ num = num()
|
|
|
+ }
|
|
|
+
|
|
|
+ areas.forEach((area)=>{
|
|
|
+ let s_ = e * area[0] + s * (1-area[0])
|
|
|
+ let e_ = e * area[1] + s * (1-area[1])
|
|
|
+ s = s_, e = e_
|
|
|
+ })
|
|
|
+
|
|
|
+ let p = e * num + s * (1-num)
|
|
|
+
|
|
|
+ str == 'loadpoints' && Potree.settings.isTest && console.log('notify pro', p, str)
|
|
|
+ deferred.notify(p)
|
|
|
+ curPercent = p
|
|
|
+ }
|
|
|
+ setTimeout(resolve, delayForNotify)
|
|
|
+ })
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+export default class VolumeComputer extends THREE.EventDispatcher{
|
|
|
+
|
|
|
+ constructor(){
|
|
|
+ super()
|
|
|
+
|
|
|
+ this.camera = new THREE.OrthographicCamera(-100,100,-100,100 , 0.01, 100);
|
|
|
+ this.camera.up.set(0,0,1);
|
|
|
+ this.view = new ExtendView
|
|
|
+
|
|
|
+ this.viewport = new Viewport(this.view,this.camera,{left:0,bottom:0,width:1,height:1})
|
|
|
+ this.renderTarget = new THREE.WebGLRenderTarget(
|
|
|
+ 1, 1,
|
|
|
+ { minFilter: THREE.LinearFilter,
|
|
|
+ magFilter: THREE.NearestFilter,
|
|
|
+ format: THREE.RGBAFormat }
|
|
|
+ );
|
|
|
+
|
|
|
+ this.material = new ExtendPointCloudMaterial();
|
|
|
+
|
|
|
+ this.material.activeAttributeName = testPoint ? "rgba" : 'prismHeight' ;// 'rgba' indices prismHeight
|
|
|
+
|
|
|
+ this.height = {up: 2, down: 1}
|
|
|
+
|
|
|
+ this.prisms = []
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ init(){
|
|
|
+ viewer.scene.addEventListener('measurement_added',(e)=>{
|
|
|
+ if(e.measurement.isPrism) this.add(e.measurement)
|
|
|
+ })
|
|
|
+ viewer.scene.addEventListener('measurement_removed',(e)=>{
|
|
|
+ if(e.measurement.isPrism) this.remove(e.measurement)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ enter(){
|
|
|
+ this.entered = true
|
|
|
+ viewer.measuringTool.history.clear() //避免撤销到测量线去
|
|
|
+ this.oldStates = {
|
|
|
+ rotAroundPoint : Potree.settings.rotAroundPoint
|
|
|
+ }
|
|
|
+ Potree.settings.rotAroundPoint = false
|
|
|
+ }
|
|
|
+
|
|
|
+ leave(){
|
|
|
+ this.entered = false
|
|
|
+ Potree.settings.rotAroundPoint = this.oldStates.rotAroundPoint
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ add(prism){
|
|
|
+ this.prisms.push(prism)
|
|
|
+ }
|
|
|
+
|
|
|
+ remove(prism){
|
|
|
+ let i = this.prisms.indexOf(prism)
|
|
|
+ i>-1 && this.prisms.splice(i,1)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ startCompute(){//test
|
|
|
+ this.enter();
|
|
|
+
|
|
|
+ let deferred = $.Deferred()
|
|
|
+ deferred.done((e)=>{
|
|
|
+ console.log('done', e)
|
|
|
+ })
|
|
|
+ deferred.fail((e)=>{
|
|
|
+ console.log('?fail????', e)
|
|
|
+ })
|
|
|
+ /* deferred.progress((e)=>{
|
|
|
+ console.log('progress', e)
|
|
|
+ }) */
|
|
|
+
|
|
|
+ this.compute(this.prisms[this.prisms.length-1], deferred )
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ cancel(){
|
|
|
+ if(this.prismComputing){
|
|
|
+ interrupt = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //法1:只用一个顶部相机(当挖方遮挡填方时不准,但好算)
|
|
|
+ async compute(prism, deferred_, getResolveResult){
|
|
|
+
|
|
|
+ if(this.prismComputing) return console.log('正在计算,请稍等')
|
|
|
+
|
|
|
+ deferred = deferred_
|
|
|
+ timestamp = Date.now()
|
|
|
+
|
|
|
+ this.prismComputing = prism
|
|
|
+ prism.computing = true
|
|
|
+
|
|
|
+ let computeFinish = (debugStr, makeit)=>{
|
|
|
+
|
|
|
+ makeit || deferred.reject(interrupt ? '主动取消' : (debugStr || ''))
|
|
|
+
|
|
|
+
|
|
|
+ this.prismComputing && (this.prismComputing.computing = false)
|
|
|
+ this.prismComputing = null
|
|
|
+ deferred = null
|
|
|
+ interrupt = false
|
|
|
+ debugStr && console.log(debugStr)
|
|
|
+
|
|
|
+ Potree.settings.displayMode = statesBefore.mode
|
|
|
+ Potree.settings.pointDensity = statesBefore.pointDensity
|
|
|
+ viewer.renderer.setRenderTarget(null);
|
|
|
+ viewer.renderer.state.reset();
|
|
|
+ viewer.renderer.setScissorTest(false);
|
|
|
+ gl.disable(gl.SCISSOR_TEST);
|
|
|
+
|
|
|
+
|
|
|
+ viewer.screenshoting = false
|
|
|
+ viewer.mainViewport.active = true
|
|
|
+ viewer.mapViewer.viewports[0].active = true
|
|
|
+ viewer.magnifier.viewport.active = true
|
|
|
+ console.log('pxPerMetric',pxPerMetric, 'boundSize',boundSize)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ let startTime = Date.now()
|
|
|
+
|
|
|
+ let pointcloud = viewer.scene.pointclouds[0]
|
|
|
+
|
|
|
+
|
|
|
+ let center = new THREE.Vector3
|
|
|
+ let boundSize = new THREE.Vector3
|
|
|
+ let bound = new THREE.Box3
|
|
|
+ horizonZ = prism.horizonZ
|
|
|
+ zMin = prism.zMin
|
|
|
+ zMax = prism.zMax
|
|
|
+ this.camera.far = zMax - zMin + 10
|
|
|
+
|
|
|
+
|
|
|
+ lowest = zMax, highest = zMin
|
|
|
+
|
|
|
+ for(let i=0;i<prism.points.length;i++){
|
|
|
+ bound.expandByPoint(prism.points[i])
|
|
|
+ }
|
|
|
+ bound.getCenter(center)
|
|
|
+ bound.getSize(boundSize)
|
|
|
+ center.z = zMax + this.camera.near + 0.1
|
|
|
+
|
|
|
+ viewer.screenshoting = true
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let pxPerMetric = math.linearClamp(Math.max(boundSize.x,boundSize.y), [3, 20, 80, 500, 2000], [300, 100, 30, 6, 3])
|
|
|
+ //let pxPerMetric = math.linearClamp(Math.max(boundSize.x,boundSize.y), [5, 20, 80, 500], [300, 200, 50, 20])
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let w = pxPerMetric * boundSize.x, h = boundSize.y * pxPerMetric
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let cW = 1 , cH = 1 //横向纵向渲染次数 超过maxWidth时分批渲染
|
|
|
+ if(w > maxWidth || h > maxWidth){
|
|
|
+ /* if(){//多数据集判断是否间隔过远,是的话返回
|
|
|
+
|
|
|
+ } */
|
|
|
+ cW = Math.ceil(w / maxWidth), cH = Math.ceil(h / maxWidth)
|
|
|
+ w /= cW, h /= cH
|
|
|
+ }
|
|
|
+ w = Math.round(w)
|
|
|
+ h = Math.round(h)
|
|
|
+ this.camera.right = w / 2
|
|
|
+ this.camera.left = -this.camera.right
|
|
|
+ this.camera.top = h / 2
|
|
|
+ this.camera.bottom = -this.camera.top
|
|
|
+ this.viewport.resolution.set(w , h)
|
|
|
+ this.renderTarget.setSize(w, h)
|
|
|
+
|
|
|
+
|
|
|
+ wDelta = boundSize.x / (w * cW )
|
|
|
+ sDelta = Math.pow(wDelta, 2)
|
|
|
+
|
|
|
+ //准备
|
|
|
+ let statesBefore = {
|
|
|
+ mode: Potree.settings.displayMode,
|
|
|
+ pointDensity:Potree.settings.pointDensity
|
|
|
+ }
|
|
|
+ Potree.settings.displayMode = 'showPointCloud'
|
|
|
+ Potree.settings.pointDensity = 'screenshot'
|
|
|
+ viewer.mainViewport.active = false //暂停渲染,否则影响这边点云的加载
|
|
|
+ viewer.mapViewer.viewports[0].active = false
|
|
|
+ viewer.magnifier.viewport.active = false //防止页面变白(似乎mainViewport被clear)
|
|
|
+ /* let maxLevels = new Map
|
|
|
+ viewer.scene.pointclouds.forEach(e=>{
|
|
|
+ maxLevels.set(e,e.maxLevel);//记录能达到的最大值
|
|
|
+ }) */
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ {
|
|
|
+ //this.material.shape = Potree.PointShape.PARABOLOID;//加上之后depth出错,后排点云会挡前排,可pick时为什么不会?
|
|
|
+
|
|
|
+ this.material.pointSizeType = 'ADAPTIVE' //node会自动根据的其level更改点大小
|
|
|
+ this.material.uniforms.minSize.value = 0.1
|
|
|
+ this.material.uniforms.orthoMaxSize.value = 5; //px
|
|
|
+
|
|
|
+ this.material.classification = pointcloud.material.classification;
|
|
|
+ this.material.recomputeClassification();
|
|
|
+
|
|
|
+ /* this.material.prisms = [prism]
|
|
|
+ this.material.uniforms.prismList = pointcloud.material.uniforms.prismList
|
|
|
+ this.material.uniforms.prismPoints = pointcloud.material.uniforms.prismPoints */
|
|
|
+ this.material.setClipBoxes(null,[],[],[],[prism])
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ this.material.shaderNeedsUpdate = true
|
|
|
+ pointcloud.updateMaterial(this.material, null, this.camera, viewer.renderer, this.viewport.resolution);
|
|
|
+
|
|
|
+
|
|
|
+ this.material.size = this.getPointsize(pointcloud.maxLevel) //0.025 米 //视图缩小(截图面积变大)后点会自动缩小,但至少是1个像素大小
|
|
|
+ }
|
|
|
+
|
|
|
+ let gl = viewer.renderer.getContext();
|
|
|
+
|
|
|
+ viewer.renderer.state.buffers.depth.setTest(this.material.depthTest);
|
|
|
+ viewer.renderer.state.buffers.depth.setMask(this.material.depthWrite);
|
|
|
+ viewer.renderer.state.setBlending(THREE.NoBlending);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let pixelCount = this.renderTarget.width * this.renderTarget.height
|
|
|
+ let buffer = new Uint8Array(4 * pixelCount);
|
|
|
+
|
|
|
+ viewer.renderer.setRenderTarget(this.renderTarget);
|
|
|
+
|
|
|
+ let results = []
|
|
|
+ let boundSize_ = boundSize.clone()
|
|
|
+ boundSize_.x /= cW, boundSize_.y /= cH
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //分批渲染:从左到右 从下到上
|
|
|
+ for(let i=0; i<cW; i++){
|
|
|
+ for(let j=0; j<cH; j++){
|
|
|
+ let infos = []
|
|
|
+
|
|
|
+ let progressStart = (i*cH + j) / (cW * cH) , progressEnd = (i*cH + j+1) / (cW * cH)
|
|
|
+
|
|
|
+ if(interrupt){
|
|
|
+ return computeFinish('23')
|
|
|
+ }
|
|
|
+ //最高处朝下看,找挖方的顶部
|
|
|
+ this.view.pitch = -Math.PI / 2;
|
|
|
+ let endPosition = new THREE.Vector3(center.x + (i-cW/2+0.5) * boundSize_.x, center.y + (j-cH/2+0.5) * boundSize_.y, center.z)
|
|
|
+ this.view.moveOrthoCamera(this.viewport, {boundSize: boundSize_, endPosition }, 0)
|
|
|
+ await this.render({name:i+'-'+j+'-第1张', infos, buffer, /* maxLevels, */ notifyArea:[[progressStart, progressEnd],[0,0.4]], computeFinish })
|
|
|
+
|
|
|
+
|
|
|
+ if(interrupt){
|
|
|
+ return computeFinish('2123')
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if(!testPoint){
|
|
|
+ //水平面朝下看,找填方
|
|
|
+ this.view.position.z = horizonZ + this.camera.near
|
|
|
+ await this.render({name:i+'-'+j+'-第2张', infos, buffer, /* maxLevels, */ notifyArea:[[progressStart, progressEnd], [0.4, 0.7]], computeFinish})
|
|
|
+ //水平面朝上看,找挖方的底部
|
|
|
+ /* this.view.position.z = horizonZ + surfaceThick //排除地面(水平面)上的点
|
|
|
+ this.view.pitch = Math.PI / 2;
|
|
|
+ await this.render({reverseRow:true,name:'第3张-'+i+'-'+j, infos, buffer,maxLevels }) *///---土堆上有草地有厚度,还是去掉吧
|
|
|
+
|
|
|
+ let result = {
|
|
|
+ index : {i,j},
|
|
|
+ allFills:[],
|
|
|
+ fillVupper:0 ,
|
|
|
+ fillVlower:0 ,
|
|
|
+ Vupper : 0,
|
|
|
+ Vlower : infos[1].Vlower, // >= infos[0].Vlower 因能找到被上方遮挡住的凹槽
|
|
|
+ allDiffHeights : [],
|
|
|
+ infos
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ let waitFill = [];
|
|
|
+ waitFill.count = 0
|
|
|
+ let isNeighbor = (a,b)=>{
|
|
|
+ return Math.abs(a.u - b.u ) <= 1 && Math.abs(a.v - b.v ) <= 1
|
|
|
+ }
|
|
|
+ let isBelongToGroup = (group, item)=>{
|
|
|
+ if(item[0])item = item[0]
|
|
|
+
|
|
|
+ let i=group.length
|
|
|
+ while(--i >=0){//从后往前
|
|
|
+ if(item.u-group[i].u > 1 && item.v-group[i].v > 1) break //距离超出,前面肯定不相邻了
|
|
|
+
|
|
|
+ if(isNeighbor(group[i], item)){
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //计算挖方(但是边缘附近垂直方向上很多点云,体积被削皮,最终得到的会比infos[0]少)
|
|
|
+
|
|
|
+ let getValue = (u,v)=>{//当到了左上边界是否需要向前几批的查找?
|
|
|
+ if(infos[0].allHeights[u] && infos[0].allHeights[u][v] != void 0 )return infos[0].allHeights[u][v]
|
|
|
+ return result.allFills[u] && result.allFills[u][v]
|
|
|
+ }
|
|
|
+
|
|
|
+ let getFill = (u,v)=>{//使用周围的八个点的平均值补洞
|
|
|
+
|
|
|
+ let neighbours = [[0,-1],[-1,0],[1,0],[0,1], [-1,-1], [1,-1], [-1,1],[1,1]]//先左右右先上后下
|
|
|
+ let i=0, c=0, sum=0
|
|
|
+
|
|
|
+
|
|
|
+ while(c<4 && i<8){
|
|
|
+ let neigh = getValue(neighbours[i][0]+u,neighbours[i][1]+v)
|
|
|
+ if(neigh != void 0){
|
|
|
+ c++;
|
|
|
+ sum += neigh
|
|
|
+ }
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ if(c>0){
|
|
|
+ sum /= c;
|
|
|
+ result.allFills[u] || (result.allFills[u] = [])
|
|
|
+ result.allFills[u][v] = sum
|
|
|
+ return sum
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ let add = (v, fill)=>{
|
|
|
+ if(v > 0){
|
|
|
+ result.Vupper += v
|
|
|
+ fill && (result.fillVupper+=v)
|
|
|
+ }else{//fill的话才会有
|
|
|
+ fill && (result.fillVlower-=v)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ let compute = async (u,v)=>{
|
|
|
+
|
|
|
+ let top = infos[0].allHeights[u][v]
|
|
|
+ //let btm = infos[2].allHeights[u][v]
|
|
|
+ let fill
|
|
|
+ if(top == void 0){
|
|
|
+ let x = endPosition.x - boundSize_.x /2 + wDelta * (u + 0.5)
|
|
|
+ let y = endPosition.y - boundSize_.y /2 + wDelta * (v + 0.5)
|
|
|
+ if(!math.isPointInArea(prism.points, null, {x,y}) ){
|
|
|
+ return //出界
|
|
|
+ }
|
|
|
+ fill = true
|
|
|
+ top = getFill(u,v)
|
|
|
+ if(top == void 0){ //左侧边缘有缺口时会出现
|
|
|
+
|
|
|
+ Common.pushToGroupAuto([{u,v}], waitFill, null, isBelongToGroup) //按邻近点分组
|
|
|
+
|
|
|
+ waitFill.count ++
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* let diff = top-btm
|
|
|
+ let useDiff
|
|
|
+ if(btm != void 0 && diff > surfaceThick ){
|
|
|
+ useDiff = diff
|
|
|
+ result.allDiffHeights.push(useDiff)
|
|
|
+ }else{
|
|
|
+ useDiff = top //从顶部一直到水平
|
|
|
+ }*/
|
|
|
+
|
|
|
+ if(waitFill.length){//补 之前没有值的邻居重新获取
|
|
|
+ if(interrupt){
|
|
|
+ return computeFinish('12312')
|
|
|
+ }
|
|
|
+ //Potree.settings.isTest && console.log('waitFill.count', waitFill.count, 'groupLen', waitFill.length, waitFill[0][0], waitFill[0][waitFill[0].length-1])
|
|
|
+ waitFill.slice().forEach(group=>{ //每个组都看看是否跟它相邻
|
|
|
+ if(isBelongToGroup(group, {u,v})){
|
|
|
+ let index = waitFill.indexOf(group)
|
|
|
+ waitFill.splice(index,1)
|
|
|
+ waitFill.count -= group.length
|
|
|
+ group.forEach(e=>{
|
|
|
+ result.allFills[e.u] || (result.allFills[e.u] = [])
|
|
|
+ result.allFills[e.u][e.v] = top //该组全部都使用该体积
|
|
|
+ })
|
|
|
+ let sum = top * group.length
|
|
|
+ add(sum, fill)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ add(top, fill)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let lastNotifyTime = Date.now()
|
|
|
+ //从左到右,从下到上一列列扫描
|
|
|
+ for (let u = 0; u < this.renderTarget.width; u++) {
|
|
|
+ for (let v = 0; v < this.renderTarget.height; v++) { //数据是从图的下到上存储的
|
|
|
+
|
|
|
+ if(interrupt){
|
|
|
+ return computeFinish('1121')
|
|
|
+ }
|
|
|
+ compute(u,v);
|
|
|
+ }
|
|
|
+ await notify([[progressStart, progressEnd], [0.7, 1]] , ()=>{return u / this.renderTarget.width }, 'compute')
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if(waitFill.length){
|
|
|
+ console.log('waitFill怎么还有??', waitFill)
|
|
|
+ }
|
|
|
+ result.Vupper *= sDelta
|
|
|
+ result.fillVlower *= sDelta
|
|
|
+ result.fillVupper *= sDelta
|
|
|
+
|
|
|
+ result.Vlower += result.fillVlower
|
|
|
+
|
|
|
+ results.push(result)
|
|
|
+ }
|
|
|
+ deferred && deferred.notify(progressEnd)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ let Vupper = 0, Vlower = 0, fillVlower = 0, fillVupper = 0
|
|
|
+ results.forEach((c)=>{
|
|
|
+ Vupper += c.Vupper; Vlower += c.Vlower;
|
|
|
+ fillVlower += c.fillVlower; fillVupper += c.fillVupper;
|
|
|
+ })
|
|
|
+
|
|
|
+ prism.setVolumeInfo({
|
|
|
+ highest,lowest,Vupper,Vlower,
|
|
|
+ })
|
|
|
+
|
|
|
+ let endTime = Date.now()
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ deferred && deferred.resolve(getResolveResult && getResolveResult())
|
|
|
+
|
|
|
+ computeFinish('finish', true)
|
|
|
+ console.log( {Vupper,Vlower, oldVupper: Vupper - fillVupper, oldVlower: Vlower - fillVlower, highest,lowest, horizonZ, wDelta}, results, this.viewport.resolution, {cW,cH}, 'cost:', endTime-startTime)//firefox用时是chrome两倍,edge和chrome差不多
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ async render({reverseRow, name, infos, buffer, /* maxLevels, */ notifyArea, computeFinish}={}){
|
|
|
+ this.view.applyToCamera(this.camera)
|
|
|
+ Potree.updatePointClouds(viewer.scene.pointclouds, this.camera, this.viewport.resolution );
|
|
|
+ let gl = viewer.renderer.getContext();
|
|
|
+ let screenshot = (name)=>{
|
|
|
+ //if(window.testScreen){
|
|
|
+ let dataUrl = Potree.Utils.renderTargetToDataUrl(this.renderTarget, this.renderTarget.width, this.renderTarget.height, viewer.renderer)
|
|
|
+
|
|
|
+ Common.downloadFile(dataUrl, (name || 'screenshot') + '-' + timestamp +'.png') //为什么图片上不是只有pickWindowSize区域有颜色??
|
|
|
+
|
|
|
+ //}
|
|
|
+ }
|
|
|
+ console.log('开始渲染', name)
|
|
|
+
|
|
|
+
|
|
|
+ let camera = this.camera
|
|
|
+ let viewport = this.viewport
|
|
|
+
|
|
|
+ return new Promise((resolve, reject)=>{
|
|
|
+
|
|
|
+ let done = async ()=>{
|
|
|
+ gl.clearColor(0, 0, 0, 0);
|
|
|
+ viewer.renderer.clear(true, true, true);
|
|
|
+
|
|
|
+ //viewer.pRenderer.renderOctree(pointcloud, nodes, this.camera, this.renderTarget ,{material:this.material});
|
|
|
+ viewer.pRenderer.render(viewer.scene.scenePointCloud, this.camera, this.renderTarget ,{material:this.material});
|
|
|
+
|
|
|
+ if(interrupt){
|
|
|
+ return (reject(), computeFinish('re 23'))
|
|
|
+ }
|
|
|
+
|
|
|
+ Potree.settings.isTest && screenshot(name);
|
|
|
+ gl.readPixels(0, 0, this.renderTarget.width , this.renderTarget.height, gl.RGBA, gl.UNSIGNED_BYTE, buffer); //这句花费最多时间 pc:2-4, 即使只有1*1像素
|
|
|
+
|
|
|
+ notifyArea.push([progressEnd1,1])
|
|
|
+
|
|
|
+ let Vupper = 0, Vlower = 0
|
|
|
+
|
|
|
+ let upperHeights = [],lowerHeights = [], allHeights = []
|
|
|
+ for (let u = 0; u < this.renderTarget.width; u++) {
|
|
|
+ let col = []
|
|
|
+
|
|
|
+ for (let v = 0; v < this.renderTarget.height; v++) {
|
|
|
+ let _v = reverseRow ? this.renderTarget.height - v - 1: v
|
|
|
+ let offset = (u + _v * this.renderTarget.width);
|
|
|
+ let rgb = buffer.slice(4 * offset, 4 * offset+3), a = buffer[4 * offset+3]
|
|
|
+ if(a == 0 /* rgb[0] == 0 && rgb[1] == 0 && rgb[2] == 0 */){ //无点
|
|
|
+ col.push(null)
|
|
|
+ }else{
|
|
|
+ let z = this.convertColorToHeight(rgb, zMin, zMax)
|
|
|
+ let h = z - horizonZ
|
|
|
+
|
|
|
+ z < lowest && (lowest = z) //上表面最低点
|
|
|
+ z > highest && (highest = z) //上表面最高点
|
|
|
+
|
|
|
+ if(h>0){
|
|
|
+ Vupper += h
|
|
|
+ upperHeights.push(h)
|
|
|
+ }else{
|
|
|
+ Vlower += -h
|
|
|
+ lowerHeights.push(h)
|
|
|
+ }
|
|
|
+ col.push(h)
|
|
|
+ }
|
|
|
+
|
|
|
+ if(interrupt){
|
|
|
+ return (reject(), computeFinish('re 023'))
|
|
|
+ }
|
|
|
+ //notify(notifyArea, (u*this.renderTarget.height + v) /(this.renderTarget.width*this.renderTarget.height))
|
|
|
+ }
|
|
|
+ allHeights.push(col)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ await notify(notifyArea, ()=>{return u / this.renderTarget.width}, 'readPixels get')
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ Vupper*=sDelta
|
|
|
+ Vlower*=sDelta
|
|
|
+
|
|
|
+ infos.push({Vupper,Vlower,sDelta,upperHeights,lowerHeights,allHeights})
|
|
|
+ resolve()
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //let finish , maxTime = 1e6
|
|
|
+ let progressEnd1 = math.linearClamp( this.camera.zoom, [1,1000], [0.8,0.2]) //放得越大,实际加载的点云相对越少,费时越少
|
|
|
+ let notifyArea_ = [...notifyArea, [0, progressEnd1]]
|
|
|
+
|
|
|
+ if(Potree.pointsLoading ){ //如果有要加载的node
|
|
|
+
|
|
|
+
|
|
|
+ let curProgress = 0, beginTime = Date.now(), unloadBefore,
|
|
|
+
|
|
|
+ tryPreLoadTime = Math.round(math.linearClamp(buffer.length/this.camera.zoom/this.camera.zoom, [50,10000],[1000,15000]))
|
|
|
+ //console.log('tryPreLoadTime',tryPreLoadTime)
|
|
|
+
|
|
|
+ let decreaseLevel = ()=>{ //降点云level
|
|
|
+ //暂时先都一起降1
|
|
|
+ let pointclouds = viewer.scene.pointclouds.filter(e=>e.visibleNodes.length)
|
|
|
+ console.log('准备decreaseLevel, numVisiblePoints', Potree.numVisiblePoints, '最小numVisiblePoints占比', 1/pointclouds.length )
|
|
|
+
|
|
|
+ pointclouds.forEach(e=>{
|
|
|
+ let percent = e.numVisiblePoints / Potree.numVisiblePoints
|
|
|
+ console.log('numVisiblePoints占总比', e.dataset_id, percent )
|
|
|
+ if(percent < 1/pointclouds.length)return
|
|
|
+
|
|
|
+ let old = e.maxLevel
|
|
|
+ let levels = e.visibleNodes.map(e=>e.getLevel())
|
|
|
+ let actMaxLevel = Math.max.apply(null, levels) //实际加载到的最高的node level
|
|
|
+ e.maxLevel = actMaxLevel - 1
|
|
|
+ console.warn(e.dataset_id,'decreaseLevel,新maxLevel', actMaxLevel - 1, '原maxlevel', old )
|
|
|
+
|
|
|
+ //this.material.size = this.getPointsize(e.maxLevel)
|
|
|
+ //this.material.size *= 2
|
|
|
+ })
|
|
|
+
|
|
|
+ }
|
|
|
+ let finish = (makeit)=>{
|
|
|
+ //viewer.removeEventListener('overPointBudget',decreaseLevel)
|
|
|
+ makeit && done()
|
|
|
+ viewer.removeEventListener('update',update)
|
|
|
+ viewer.removeEventListener('pointsLoaded',loaded)
|
|
|
+ }
|
|
|
+ let update = ()=>{
|
|
|
+ Potree.updatePointClouds(viewer.scene.pointclouds, camera, viewport.resolution );
|
|
|
+ if( Potree.numVisiblePoints > maxPointBudge * 0.9){
|
|
|
+ decreaseLevel()
|
|
|
+ }
|
|
|
+
|
|
|
+ notify(notifyArea_, ()=>{
|
|
|
+ /* console.log('unloadedGeometry',Potree.unloadedGeometry.length)
|
|
|
+
|
|
|
+ return curProgress */
|
|
|
+
|
|
|
+
|
|
|
+ /* curProgress = THREE.Math.clamp(Potree.numVisiblePoints/predictPointCount,curProgress,1) //如果decreaseLevel了点会减少,也不管它,就停住吧,反正很快就加载好了
|
|
|
+ return curProgress */
|
|
|
+
|
|
|
+
|
|
|
+ if(!unloadBefore && Date.now() - beginTime < tryPreLoadTime){
|
|
|
+ curProgress = (Date.now() - beginTime) / tryPreLoadTime * 0.5
|
|
|
+ }else{
|
|
|
+ if(!unloadBefore){
|
|
|
+ unloadBefore = Potree.unloadedGeometry.length
|
|
|
+ //console.log('unloadBefore', unloadBefore)
|
|
|
+ }
|
|
|
+ unloadBefore = Math.max(Potree.unloadedGeometry.length, unloadBefore) //unloadedGeometry 刚开始可能越涨越多 - - 所以进度条会卡住
|
|
|
+
|
|
|
+ let curProgress_ = 0.5 + 0.5 * (1 - Potree.unloadedGeometry.length / unloadBefore )
|
|
|
+
|
|
|
+ curProgress = Math.max(curProgress,curProgress_)
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //console.log('curProgress ', curProgress, 'numVisiblePoints', Potree.numVisiblePoints, 'unloadedGeometry',Potree.unloadedGeometry.length)
|
|
|
+
|
|
|
+ return curProgress
|
|
|
+ }, 'loadpoints')
|
|
|
+
|
|
|
+
|
|
|
+ interrupt && (finish(false), reject(), computeFinish('正在loadPoints,unloadedGeometry个数'+Potree.unloadedGeometry.length )) //曾经遇到遇到bug 不发送pointsLoaded 一直update 一直有3个unloadedGeometry加载不了。。 是否为每个加载的node加个计时?
|
|
|
+ }
|
|
|
+ viewer.addEventListener('update',update)
|
|
|
+
|
|
|
+ let loaded = ()=>{ //点云加载完时(不一定准确)
|
|
|
+ console.warn('加载完毕', ' numVisiblePoints', Potree.numVisiblePoints )
|
|
|
+ finish(true)
|
|
|
+ }
|
|
|
+ viewer.addEventListener('pointsLoaded', loaded)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /* let predictPointCount = Math.round(math.linearClamp(buffer.length, [1e5, maxWidth*maxWidth*4],[1e6, maxPointBudge ]))//最终要加载到的数目
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+ /* let overTime = ()=>{
|
|
|
+ if(document.hidden){
|
|
|
+ return setTimeout(overTime, maxTime)
|
|
|
+ }
|
|
|
+ if(!finish)console.warn('超时, numVisiblePoints', Potree.numVisiblePoints)
|
|
|
+ dealDone()
|
|
|
+ }
|
|
|
+
|
|
|
+ setTimeout(overTime, maxTime) */
|
|
|
+ }else{
|
|
|
+ notify(notifyArea_, 1)
|
|
|
+ done()
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ convertColorToHeight(color, zMin, zMax){
|
|
|
+ let percent = 0
|
|
|
+ let a = 1
|
|
|
+ for(let i=0;i<3;i++){
|
|
|
+ a = i == 2 ? a/255 : a / 256
|
|
|
+ percent += a * color[i];
|
|
|
+ }
|
|
|
+
|
|
|
+ return (zMax - zMin) * percent + zMin;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ getPointsize(maxLevel){//尽量铺满 size单位是米,需要和点云缝隙同宽
|
|
|
+
|
|
|
+ return 0.2
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let s = /* this.material.spacing */ 100 / Math.pow(2, maxLevel - 1) //adaptive的话在shader中已经乘了spacing
|
|
|
+ console.log('getPointsize', s, 'maxLevel',maxLevel)
|
|
|
+ return s //会不会直接返回1就好?
|
|
|
+ //不能太小。假如gl_pointsize计算结果是0.1,那么显示就是1px;当level将级后,放大2倍为0.2,也还是1px,看起来就稀疏了。所以要保证这个数在最大level时gl_pointsize计算结果差不多是1px.(虽然我会在渲染大尺寸面积时降画布尺寸,但不能保证刚刚好,且因点云个数不可控不会降尺寸太狠)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ download(prisms){
|
|
|
+
|
|
|
+ if(this.prisms.length == 0){
|
|
|
+ return null
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ var visiPointclouds = viewer.scene.pointclouds.filter(e=> Potree.Utils.getObjVisiByReason(e, 'datasetSelection'))
|
|
|
+ let data = {
|
|
|
+ transformation_matrix: visiPointclouds.map((cloud)=>{
|
|
|
+ let data = {
|
|
|
+ id: cloud.dataset_id,
|
|
|
+ matrix: new THREE.Matrix4().elements, //参照downloadNoCrop,给默认值,表示没有最外层裁剪
|
|
|
+ visiMatrixes: prisms.map(prism=>{return {
|
|
|
+ matrix: prism.getTransformationMatrix(cloud).elements,//剪裁框
|
|
|
+ points: prism.points.map(p=>{return {x:p.x, y:p.y}})
|
|
|
+ }}),
|
|
|
+ modelMatrix:(new THREE.Matrix4).copy(cloud.transformMatrix).transpose().elements
|
|
|
+ }
|
|
|
+ return data
|
|
|
+ }) ,
|
|
|
+ aabb: "b-12742000 -12742000 -12742000 12742000 12742000 12742000" //剪裁空间
|
|
|
+ }
|
|
|
+
|
|
|
+ return data
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //体积多边形棱柱,z需要变换到-0.5 ~ 0.5
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+
|
|
|
+ 渲染像素pxPerMetric越高,边缘越细腻、准确; 点size越小,越不容易遮盖住下方点云,造成体积偏大。但会不会有底部点云透出来?
|
|
|
+
|
|
|
+
|
|
|
+ 该版本计算用到两张截图,第一张为整体的俯视图,包括挖方和填方;第二张仅包含填方。
|
|
|
+ 缺陷:在补洞时,只利用第一张图的结果,所以整体的挖方会偏多,填方偏少(因为在挖方填方同时存在时,没有分别补挖方和填方,而是放在一起,而上方可能遮盖下方的)
|
|
|
+ 尝试过第一张只绘制挖方部分,但是在遍历时就需要多遍历一次,测得的用时更多一些,结果却没变化
|
|
|
+ 也尝试过补洞时挖方填方分别补各自的,但这样要考虑更多的东西,用时多了两倍以上。(因为目前规定无论范围内实际有没有点云都补上,非挖即填;如果分开的话,填方边缘也都补上肯定是错的,无法找到挖和填的界限,无法分清是只有挖还是填还是两者均有。如果要探测周围点,耗时很多)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 如果数据集修改了,土方量需要手动重算。所以场景刷新后显示的体积有可能是错的。
|
|
|
+ 之前记的,太麻烦了,延期处理:
|
|
|
+ 1 一般情况:我需要记录每个土方量范围内存在的数据集的位置,如
|
|
|
+
|
|
|
+ {datasetId:"1745733728954093568", location:[113.59550104511611, 22.36677932324081, 0] ,orientation: 0.1}
|
|
|
+
|
|
|
+ 如果刷新后土方量范围内存在的数据集发生改变,将重算。
|
|
|
+
|
|
|
+
|
|
|
+ 2 其他特殊情况,如裁剪,难以知道裁剪了哪块,所以只能全部重算
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ */
|