panoEditor.js 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496
  1. import * as THREE from "../../../../libs/three.js/build/three.module.js";
  2. import math from "../../utils/math.js"
  3. import Common from '../../utils/Common.js'
  4. import {LineDraw, MeshDraw} from "../../utils/DrawUtil.js";
  5. import {ExtendView} from "../../../viewer/ExtendView.js";
  6. import Viewport from "../../viewer/Viewport.js";
  7. import Sprite from "../../objects/Sprite.js";
  8. import {transitions, easing, lerp} from '../../utils/transitions.js'
  9. import {TransformControls} from "../../objects/tool/TransformControls.js";
  10. import SplitScreen from "../../utils/SplitScreen.js"
  11. import DepthBasicMaterial from "../../materials/DepthBasicMaterial.js";
  12. import {BoxVolume} from '../../../utils/VolumeNew.js'
  13. const clickPanoToDisLink = false;//是否在编辑漫游点连接时,通过点击漫游点能断开连接
  14. let images360, Alignment, SiteModel, suggestCircleMat
  15. const texLoader = new THREE.TextureLoader()
  16. texLoader.crossOrigin = "anonymous"
  17. const rotQua = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), Math.PI)
  18. const lineMats = {}
  19. const circleMats = {}
  20. const renderOrders = {
  21. circleSelected:3,
  22. circle:2,
  23. line:1,
  24. }
  25. const pointColor = {
  26. /* selected:"#c80",
  27. default:'#1ac' */
  28. selected:"#c60",
  29. default:'#17c'
  30. }
  31. const opacitys = {//点云透明度
  32. 'topView':{
  33. default:0.4,
  34. selected: 0.6
  35. },
  36. 'sideView':{//侧面重叠概率高
  37. default:0.2,
  38. selected: 0.5
  39. },
  40. }//调这么低是因为有的重叠边缘太亮了
  41. const cameraProps = [
  42. {
  43. name : 'top',
  44. axis:["x","y"],
  45. direction : new THREE.Vector3(0,0,-1), //镜头朝向
  46. openCount:0,
  47. },
  48. {
  49. name : 'right',
  50. axis:["y","z"],
  51. direction : new THREE.Vector3(1,0,0),
  52. openCount:0,
  53. },
  54. {
  55. name : 'mainView',
  56. openCount:0,
  57. }
  58. ]
  59. class PanoEditor extends THREE.EventDispatcher{
  60. constructor(){
  61. super()
  62. this.panoGroup = [], //分组
  63. this.viewports = {},
  64. this.panoLink = {},
  65. this.panoMeshs = new THREE.Object3D,
  66. this.lineMeshes = new THREE.Object3D
  67. this.views = {}
  68. this.cameras = {}
  69. this.orthoCamera = new THREE.OrthographicCamera(-100, 100, 100, 100, 0.01, 10000)
  70. this.orthoCamera.up.set(0,0,1)
  71. this.selectedPano;
  72. this.selectedGroup;
  73. this.operation;
  74. this.visiblePanos = []
  75. this.suggestLines = []
  76. }
  77. init(){
  78. {//init lineMats
  79. lineMats.default = LineDraw.createFatLineMat({
  80. color: '#eeeeee',
  81. lineWidth: 2,
  82. depthTest:false
  83. })
  84. lineMats.hovered = LineDraw.createFatLineMat({
  85. color: '#00c8af',
  86. lineWidth: 2,
  87. depthTest:false
  88. })
  89. lineMats.selected = LineDraw.createFatLineMat({
  90. color: '#00c8af',
  91. lineWidth: 3,
  92. depthTest:false
  93. })
  94. lineMats.suggestLink = LineDraw.createFatLineMat({
  95. color: '#ff2222',
  96. lineWidth: 4, dashed:true,
  97. depthTest:false
  98. })
  99. }
  100. suggestCircleMat = new THREE.MeshBasicMaterial({
  101. map: texLoader.load(Potree.resourcePath+'/textures/whiteCircle.png' ) ,
  102. color:'#ff2222', transparent:true,
  103. depthTest:false, depthWrite:false
  104. })
  105. this.initViews()
  106. /* {
  107. this.box = new BoxVolume({
  108. clip:true
  109. })
  110. this.box.clipTask = ClipTask['SHOW_INSIDE_Big' ]
  111. this.box.name = "panoEditClipBox";
  112. } */
  113. viewer.addEventListener('allLoaded',()=>{
  114. images360 = viewer.images360
  115. Alignment = viewer.modules.Alignment
  116. SiteModel = viewer.modules.SiteModel
  117. this.panoMeshs.name = 'panoMeshs'
  118. viewer.scene.scene.add(this.panoMeshs)
  119. this.lineMeshes.name = 'lineMeshes'
  120. viewer.scene.scene.add(this.lineMeshes)
  121. Potree.settings.ifShowMarker = false
  122. {
  123. this.transformControls = new TransformControls(viewer.mainViewport.camera, viewer.renderArea,{
  124. dontHideWhenFaceCamera: true,
  125. rotFullCircle:true
  126. });
  127. this.transformControls.setSize(1.5)
  128. viewer.scene.scene.add(this.transformControls)
  129. this.transformControls._gizmo.hideAxis = {/* translate:['x','y'], */ rotate:['x','y','e'] }
  130. this.transformControls.setRotateMethod(2)
  131. this.fakeMarkerForTran = new THREE.Mesh(new THREE.BoxBufferGeometry(0.3,0.3,0.3) , new THREE.MeshBasicMaterial({
  132. color:"#FFFFFF", opacity:0.4, transparent:true, visible:false
  133. }));//一个看不见的mesh,只是为了让transformControls移动点云
  134. viewer.scene.scene.add(this.fakeMarkerForTran)
  135. let afterMoveCircle = (type)=>{
  136. if(type == 'position'){
  137. let moveVec = new THREE.Vector3().subVectors(this.fakeMarkerForTran.position, this.fakeMarkerForTran.oldState.position)
  138. this.selectedClouds.forEach(cloud=>Alignment.translate(cloud, moveVec))
  139. }else{
  140. let center = this.selectedPano.position;
  141. let forward = new THREE.Vector3(0,1,0);
  142. let vec1 = forward.clone().applyQuaternion(this.fakeMarkerForTran.oldState.quaternion)
  143. let vec2 = forward.clone().applyQuaternion(this.fakeMarkerForTran.quaternion)
  144. let diffAngle = math.getAngle(vec1,vec2,'z')
  145. this.selectedClouds.forEach(cloud=>{
  146. Alignment.rotateAround(center, cloud, null, diffAngle)
  147. })
  148. }
  149. this.fakeMarkerForTran.oldState = {
  150. position: this.fakeMarkerForTran.position.clone(),
  151. quaternion: this.fakeMarkerForTran.quaternion.clone(),
  152. }
  153. Alignment.history.beforeChange(this.selectedClouds)
  154. }
  155. this.fakeMarkerForTran.addEventListener('position_changed', afterMoveCircle.bind(this,'position'))
  156. this.fakeMarkerForTran.addEventListener("rotation_changed", afterMoveCircle.bind(this,'rotation') )
  157. this.transformControls.addEventListener('transform_end',()=>{
  158. Alignment.history.afterChange(this.selectedClouds)
  159. })
  160. Alignment.history.addEventListener('undo',()=>{
  161. this.updateTranCtl()
  162. })
  163. }
  164. this.initPanoLink()
  165. this.addPanoMesh()
  166. viewer.scene.pointclouds.forEach(e=>{
  167. e.material.color = pointColor.default
  168. })
  169. viewer.setEDLEnabled(true) //为了降一倍的绘制. 同时用描边增强立体感,弥补点云稀疏
  170. viewer.setEDLRadius(3)
  171. viewer.setEDLStrength(0.02)
  172. this.switchView('top')
  173. {//默认选择一个楼层
  174. let panoVisiReady, siteModelReady;
  175. let floorInit = ()=>{
  176. if(!panoVisiReady || !siteModelReady)return
  177. setTimeout(()=>{
  178. if(this.currentFloor == 'all'){//还未选择楼层的话
  179. let floor = SiteModel.entities.find(e=>e.buildType == 'floor' && e.panos.length) //选择有漫游点的一层
  180. if(!floor){
  181. floor = 'all' //SiteModel.entities.find(e=>e.buildType == 'floor')
  182. console.log('没有一层有漫游点?!')
  183. }
  184. console.log('initDataDone')
  185. console.log('gotoFloor 1')
  186. this.gotoFloor(floor)
  187. }
  188. },1) //2d那边用了nextTick ,so setTimeout here
  189. }
  190. SiteModel.bus.addEventListener('initDataDone',()=>{
  191. siteModelReady = true;
  192. floorInit()
  193. },{once:true})
  194. this.addEventListener('panoVisiReady',()=>{//2d初始化完成,才可以由3d修改pano显示 (因为在之前2d会给每个pano传来显示的消息,在这之前的修改都会别覆盖)
  195. panoVisiReady = true
  196. floorInit()
  197. },{once:true})
  198. }
  199. Alignment.bus.addEventListener('switchHandle', this.updateCursor.bind(this))
  200. viewer.addEventListener('global_click',(e)=>{
  201. if(e.button === THREE.MOUSE.RIGHT){//取消旋转和平移
  202. //console.log('right click',e)
  203. this.setLinkOperateState('addLink',false)
  204. this.setLinkOperateState('removeLink',false)
  205. }else if(this.clickToZoomInEnabled){
  206. if(this.activeViewName == 'mainView'){
  207. viewer.controls.zoomToLocation(e.mouse)
  208. }else{
  209. this.zoomIn(e.intersect.orthoIntersect, e.pointer)
  210. }
  211. this.setZoomInState(false)
  212. }
  213. })
  214. /* {//旋转时的辅助线--绕某个点旋转的版本
  215. this.rotGuideLine = LineDraw.createLine([], {color:'#aaffee'})
  216. this.rotGuideLine.visible = false
  217. this.rotGuideLine.name = 'rotGuideLine'
  218. this.rotGuideLine.renderOrder = renderOrders.line
  219. viewer.scene.scene.add(this.rotGuideLine)
  220. let startPoint
  221. Alignment.bus.addEventListener('rotateStart', (e)=>{
  222. startPoint = e.startPoint
  223. })
  224. Alignment.bus.addEventListener('rotate', (e)=>{
  225. LineDraw.updateLine(this.rotGuideLine, [startPoint, e.endPoint] )
  226. this.rotGuideLine.visible = true
  227. })
  228. viewer.fpControls.addEventListener("end",(e)=>{
  229. startPoint = null
  230. this.rotGuideLine.visible = false
  231. })
  232. } */
  233. {//连接时的辅助线
  234. this.linkGuideLine = LineDraw.createLine([], {color:'#ddd', deshed:true, dashSize:0.1,gapSize:0.05, depthTest:false})
  235. this.linkGuideLine.visible = false
  236. this.linkGuideLine.name = 'linkGuideLine'
  237. viewer.scene.scene.add(this.linkGuideLine)
  238. this.linkGuideLine.renderOrder = renderOrders.line
  239. let update = (e)=>{
  240. if(this.operation != 'addLink' || this.activeViewName != 'top' && this.activeViewName != 'mainView' ||!this.selectedPano){
  241. return this.linkGuideLine.visible = false
  242. }
  243. let endPos
  244. if(this.activeViewName == 'top' ){
  245. endPos = e.intersect.orthoIntersect.clone().setZ(this.selectedPano.position.z)
  246. }else if(this.activeViewName == 'mainView' ){
  247. if(!e.intersect || !e.intersect.point)return
  248. endPos = e.intersect.point.position
  249. }
  250. LineDraw.updateLine(this.linkGuideLine, [this.selectedPano.position, endPos] )
  251. this.linkGuideLine.visible = true
  252. viewer.dispatchEvent('content_changed')
  253. }
  254. viewer.addEventListener('global_mousemove', (e)=>{
  255. update(e)
  256. })
  257. //this.addEventListener('updateLinkGuideLine', update)
  258. }
  259. /*
  260. viewer.inputHandler.addEventListener('keydown', (e)=>{
  261. if(e.event.key == "r" ){
  262. this.setTranMode('rotate')
  263. }else if(e.event.key == "t"){
  264. this.setTranMode('translate')
  265. }
  266. }) */
  267. /* {
  268. viewer.addEventListener('camera_changed', (e)=>{
  269. Common.intervalTool.isWaiting('updatePointLevels', ()=>{
  270. this.updatePointLevels()
  271. }, 1050)
  272. })
  273. setTimeout(()=>{
  274. this.updatePointLevels()
  275. }, viewer.scene.pointclouds.length*150) //等待差不多updat出了正确的visibleNode时
  276. } */
  277. this.panoReposCallback = ()=>{
  278. viewer.controls.setTarget(this.selectedPano.position) //3d时绕其为中心转动
  279. }
  280. })
  281. }
  282. setTranMode(mode){//rotate or translate
  283. console.log('setTranMode',mode)
  284. this.tranMode = mode
  285. if(this.activeViewName == 'mainView'){
  286. mode && this.transformControls.setMode(mode)
  287. this.updateTranCtl()
  288. }else{
  289. Alignment.switchHandle(mode)
  290. }
  291. this.updateIntersectEnable()
  292. }
  293. updateIntersectEnable(){
  294. //侧面容易因intersect卡住, 如果非必要关闭intersect. 3d页面也会卡,但controls需要所以不能去掉
  295. Potree.settings.intersectWhenHover = !!(this.activeViewName == 'mainView' || this.selectedPano && this.tranMode )
  296. }
  297. updateTranCtl(){// 设置3D页面的transformControls相关
  298. if(!this.tranMode || !this.selectedPano || this.activeViewName != 'mainView' ) {
  299. return this.transformControls.detach()
  300. }else if(this.checkIfAllLinked({group:this.selectedGroup})){
  301. this.dispatchEvent('needToDisConnect')
  302. return this.transformControls.detach()
  303. }
  304. this.transformControls.attach(this.fakeMarkerForTran)
  305. let {position, quaternion} = this.getPanoPose(this.selectedPano);
  306. this.fakeMarkerForTran.position.copy(position)
  307. this.fakeMarkerForTran.quaternion.copy(quaternion)
  308. this.fakeMarkerForTran.oldState = {
  309. position: position.clone(),
  310. quaternion: quaternion.clone(),
  311. }
  312. }
  313. //////////////////////////////////
  314. initViews(){
  315. this.splitScreenTool = new SplitScreen
  316. this.targetPlane = viewer.mainViewport.targetPlane = new THREE.Plane()
  317. this.shiftTarget = viewer.mainViewport.shiftTarget = new THREE.Vector3 //project在targetPlane上的位置
  318. for(let i=0;i<2;i++){
  319. let prop = cameraProps[i];
  320. let view = new ExtendView()
  321. this.views[prop.name] = view
  322. this.cameras[prop.name] = this.orthoCamera
  323. view.name = prop.name
  324. view.direction = prop.direction
  325. }
  326. this.views.mainView = viewer.mainViewport.view
  327. this.cameras.mainView = viewer.mainViewport.camera
  328. }
  329. switchView(name ){//替换view和camera到mainViewport
  330. let view = this.views[name]
  331. let camera = this.cameras[name]
  332. let prop = cameraProps.find(e=>e.name == name)
  333. let {boundSize, center} = viewer.bound
  334. this.lastViewName = this.activeViewName
  335. this.activeViewName = name
  336. let lastView = this.views[this.lastViewName]
  337. let lastCamera = this.cameras[this.lastViewName]
  338. viewer.mainViewport.view = view
  339. viewer.mainViewport.camera = camera
  340. if(lastCamera)lastView.zoom = lastCamera.zoom
  341. this.targetPlane.setFromNormalAndCoplanarPoint( view.direction.clone(), center )
  342. this.targetPlane.projectPoint(view.position, this.shiftTarget ) //target转换到过模型中心的平面,以保证镜头一定在模型外
  343. view.position.copy(this.splitScreenTool.getPosOutOfModel(viewer.mainViewport))
  344. if(view.zoom)camera.zoom = view.zoom//恢复上次的zoom
  345. viewer.updateScreenSize({forceUpdateSize:true})//更新camera aspect left等
  346. this.updateCursor()
  347. if(name == 'mainView'){
  348. viewer.mainViewport.alignment = null
  349. let changeMat = ()=>{
  350. viewer.scene.pointclouds.forEach(e=>{
  351. e.material.activeAttributeName = 'rgba'
  352. e.material.useFilterByNormal = false
  353. e.changePointOpacity(1 )
  354. })
  355. }
  356. /* if(prop.openCount == 0){ //点数较多时,首次转到3D视角会卡顿,因为要切换材质。
  357. let delay1 = THREE.Math.clamp(viewer.scene.pointclouds.length*0.5, 1, 200)
  358. setTimeout(()=>{
  359. this.activeViewName == 'mainView' && changeMat()
  360. },delay1)
  361. //console.log('switchview',delay1 )
  362. }else{ */
  363. changeMat()
  364. //}
  365. Potree.Utils.updateVisible(viewer.reticule, 'force', true)
  366. if(lastView){//2d->3d
  367. view.copy(lastView)
  368. let direction = view.direction
  369. let panos = images360.panos.filter(e=>e.circle.visible)
  370. let nearestPano = Common.sortByScore(panos , [], [(pano)=>{
  371. let vec = new THREE.Vector3().subVectors(pano.position, view.position);
  372. return -vec.dot(direction);
  373. }], true);
  374. //console.log('最近',nearestPano )
  375. if(nearestPano && nearestPano[0] ){ //尽量不变画面范围,使pano点保持原位,转换到mainView
  376. let halfHeight = lastCamera.top/lastCamera.zoom
  377. let dis = halfHeight / Math.tan( THREE.Math.degToRad(camera.fov/2))
  378. view.position.add(direction.clone().multiplyScalar(-nearestPano[0].score - dis))
  379. //console.log('getCloser', -nearestPano[0].score - dis)
  380. this.lastDisToPano = dis //记录一下
  381. }
  382. }
  383. viewer.fpControls.lockKey = false
  384. }else{
  385. if(this.lastViewName == 'mainView'){//3d->2d
  386. let direction = lastView.direction
  387. let panos = images360.panos.filter(e=>e.circle.visible)
  388. //尽量靠近画布中心,且距离相机较近
  389. let nearestPano = Common.sortByScore(panos , [], [(pano)=>{
  390. let vec = new THREE.Vector3().subVectors(pano.position, lastView.position);
  391. let dis = vec.dot(direction);
  392. return dis < 0 ? dis * 10 : - dis
  393. },(pano)=>{
  394. let vec = new THREE.Vector3().subVectors(pano.position, lastView.position);
  395. let angle = vec.angleTo(direction)
  396. return - angle * 70
  397. }], true);
  398. //目前还存在的问题就是不知selectedPano和最近点的取舍
  399. //console.log('panos',nearestPano )
  400. if(nearestPano && nearestPano[0] ){
  401. //console.log('nearestPano',nearestPano[0].item.id )
  402. let pos1 = nearestPano[0].item.position.clone()
  403. let pos2 = pos1.clone()
  404. let dis = new THREE.Vector3().subVectors(nearestPano[0].item.position, lastView.position).dot(direction) //-nearestPano[0].score
  405. //根据2d->3d的式子逆求zoom
  406. let halfHeight = Math.abs(dis) * Math.tan( THREE.Math.degToRad(lastCamera.fov/2))
  407. camera.zoom = camera.top / halfHeight
  408. camera.updateProjectionMatrix()
  409. if(name == 'right'){//侧视图
  410. view.direction = direction.clone().setZ(0) //水平方向设定为3d的方向
  411. this.targetPlane.setFromNormalAndCoplanarPoint( view.direction.clone(), center )
  412. this.targetPlane.projectPoint(view.position, this.shiftTarget ) //target转换到过模型中心的平面,以保证镜头一定在模型外
  413. view.position.copy(this.splitScreenTool.getPosOutOfModel(viewer.mainViewport))
  414. }
  415. view.applyToCamera(camera)//update
  416. pos1.project(lastCamera)
  417. pos2.project(camera)
  418. //目标是找到画面上最接近中心的一点(最好是漫游点,不然就是点云),让其在转换画面后在画面上的位置不变。万一找到的点不在屏幕中(比如当屏幕中没点云时),就默认让那个点移动到屏幕中央,也就是假设当前它pos1在屏幕中央位置。
  419. //
  420. if(pos1.z>1){
  421. console.warn('选取的点在相机背后了!?')
  422. }
  423. //如果最近点超出屏幕范围 (-1,1), 最好将其拉到边缘,甚至居中 。这样屏幕上就不会没有漫游点了
  424. let bound = 0.9
  425. pos1.x = THREE.Math.clamp(pos1.x, -bound, bound)
  426. pos1.y = THREE.Math.clamp(pos1.y, -bound, bound)
  427. let vecOnscreen = new THREE.Vector3().subVectors(pos1,pos2)
  428. let moveVec = Potree.Utils.getOrthoCameraMoveVec(vecOnscreen, camera )
  429. //console.log('pos1', pos1)
  430. view.position.sub(moveVec)
  431. }
  432. }else{
  433. if(prop.openCount == 0){//至多执行一次
  434. this.viewportFitBound(name, boundSize, center)
  435. }
  436. }
  437. viewer.scene.pointclouds.forEach(e=>{
  438. e.material.activeAttributeName = 'color'
  439. e.material.useFilterByNormal = true
  440. let opaProp = name == 'top' ? opacitys.topView : opacitys.sideView
  441. if(this.selectedPano && this.selectedClouds.includes(e) ){
  442. e.changePointOpacity(opaProp.selected,true)
  443. e.material.color = pointColor.selected;
  444. }else{
  445. e.changePointOpacity(opaProp.default,true)
  446. e.material.color = pointColor.default;
  447. }
  448. })
  449. Potree.Utils.updateVisible(viewer.reticule, 'force', false)
  450. if(name == 'top') viewer.mainViewport.alignment = {rotate:true,translate:true};
  451. if(name == 'right'){
  452. viewer.mainViewport.alignment = {translate:true, rotateSide:true, translateVec:new THREE.Vector3(0,0,1)}; //只能上下移动
  453. viewer.mainViewport.rotateSide = true
  454. }else{
  455. viewer.mainViewport.rotateSide = false
  456. }
  457. viewer.fpControls.lockKey = true
  458. }
  459. this.updateTranCtl()
  460. this.setTranMode(this.tranMode) // update
  461. this.setZoomInState(false) //取消放大模式
  462. //this.updatePointLevels()
  463. this.updateIntersectEnable()
  464. prop.openCount ++;
  465. }
  466. viewportFitBound(){ //使一个viewport聚焦在某个范围
  467. if(viewer.mainViewport.resolution.x == 0 || viewer.mainViewport.resolution.y == 0){
  468. return setTimeout(()=>{
  469. this.viewportFitBound()
  470. },10)
  471. }
  472. this.gotoFloor(this.currentFloor, true, 0, null, true)
  473. }
  474. rotateSideCamera(angle){//侧视图绕模型中心水平旋转
  475. this.splitScreenTool.rotateSideCamera(viewer.mainViewport, angle)
  476. }
  477. zoomIn(intersect, pointer){
  478. let camera = viewer.mainViewport.camera
  479. let endZoom = 200
  480. //this.orthoMoveFit(intersect, {endZoom:viewer.mainViewport.camera.zoom < aimZoom ? aimZoom : null} , 300)
  481. let startZoom = camera.zoom
  482. if(startZoom >= endZoom){return}
  483. viewer.mainViewport.view.zoomOrthoCamera(camera, endZoom, pointer, 300)
  484. }
  485. orthoMoveFit(pos, info, duration){
  486. var margin = {x:200, y:230}
  487. this.splitScreenTool.viewportFitBound(viewer.mainViewport, info.bound, pos, duration, margin )
  488. }
  489. setZoomInState(state, informinformBy2d){//是否点击后可放大
  490. //if(state && this.activeViewName == 'mainView')return console.log('3D不可放大')
  491. this.clickToZoomInEnabled = !!state
  492. if(state){
  493. viewer.dispatchEvent({type : "CursorChange", action : "add", name:"zoomInCloud"} )
  494. }else{
  495. viewer.dispatchEvent({type : "CursorChange", action : "remove", name:"zoomInCloud" })
  496. }
  497. if(!state && !informinformBy2d){
  498. this.dispatchEvent({type:'operationCancel', operation: 'zoomIn'})
  499. }
  500. }
  501. gotoFloor(floor, force, duration = 600, informBy2d, fitBound=true){// 选择不同楼层, 切换点位显示。 'all'为全部显示
  502. floor = floor || 'all'
  503. if(this.currentFloor == floor && !force)return
  504. if(this.currentFloor != floor){//如果楼层没变,不修改可视
  505. //let pointclouds = viewer.findPointcloudsAtFloor(floor)
  506. let panos = floor == 'all' ? viewer.images360.panos : floor.panos
  507. viewer.images360.panos.forEach(pano=>{
  508. let v = panos.includes(pano)
  509. this.switchPanoVisible(pano,v)
  510. })
  511. }
  512. this.updateLinesVisible()
  513. //切换楼层时清空选择状态
  514. if(this.selectedPano && floor != 'all' && !floor.panos.includes(this.selectedPano)){
  515. this.selectedPano.circle.dispatchEvent('click')
  516. }
  517. if(this.selectedLine){
  518. this.selectedLine.dispatchEvent('click')
  519. }
  520. let bound, center
  521. if(floor == 'all'){
  522. bound = viewer.images360.bound.bounding
  523. center = viewer.images360.bound.center
  524. }else{
  525. bound = this.getPanosBound(floor)
  526. center = bound.getCenter(new THREE.Vector3())
  527. if(floor.panos.length == 0)console.log(floor.name, 'floor无漫游点' )
  528. }
  529. if(this.activeViewName != 'mainView' ){
  530. fitBound && this.orthoMoveFit(center, {bound}, duration)
  531. }else if(this.activeViewName == 'mainView'){
  532. //if(floor != 'all'){ //切换一下位置,因为原处点云会消失
  533. //viewer.scene.view.setView({position:center, duration })
  534. viewer.focusOnObject({boundingBox:bound},'boundingBox')
  535. //}
  536. }
  537. this.currentFloor = floor
  538. //if(!informBy2d){ //注释原因:2d居然不会自己变
  539. this.dispatchEvent({type:'changeFloor', floor})
  540. //}
  541. }
  542. getPanosBound(floor){
  543. if(!floor.panosBound){
  544. if(floor.panos.length == 0){
  545. floor.panosBound = viewer.images360.bound.bounding.clone()
  546. }else{
  547. let minSize = new THREE.Vector3(10,10,10)
  548. let bound = math.getBoundByPoints(floor.panos.map(e=>e.position), minSize)
  549. floor.panosBound = bound.bounding
  550. }
  551. }
  552. return floor.panosBound
  553. }
  554. switchPanoVisible(pano, v, informBy2d){
  555. //console.log(pano.id,v)
  556. pano.circle.visible = v
  557. Potree.Utils.updateVisible(pano, 'panoEditor', v)
  558. Potree.Utils.updateVisible(pano.pointcloud, 'panoEditor', v)
  559. if(v){
  560. this.visiblePanos.includes(pano) || this.visiblePanos.push(pano)
  561. }else{
  562. let index = this.visiblePanos.indexOf(pano);
  563. index>-1 && this.visiblePanos.splice(index,1)
  564. }
  565. if(informBy2d){
  566. this.dispatchEvent('panoVisiReady')
  567. this.updateLinesVisible()
  568. }
  569. informBy2d || this.dispatchEvent({type:"switchPanoVisible", pano, v})
  570. /* {
  571. setTimeout(()=>{
  572. Common.intervalTool.isWaiting('updatePointLevels2', ()=>{
  573. this.updatePointLevels()
  574. }, 50)
  575. },1)//等update过visibleNodes
  576. } */
  577. }
  578. updateLinesVisible(){
  579. this.lineMeshes.children.forEach(line=>{
  580. let names = line.name.split('-')
  581. var pano0 = images360.getPano(names[0])
  582. var pano1 = images360.getPano(names[1])
  583. line.visible = this.visiblePanos.includes(pano0) || this.visiblePanos.includes(pano1)
  584. })
  585. }
  586. updateCursor(){
  587. let cursor
  588. if(this.activeViewName == 'mainView' || !this.selectedPano){
  589. cursor = null
  590. }else{
  591. cursor = Alignment.handleState
  592. }
  593. if(cursor == 'rotate'){
  594. viewer.dispatchEvent({
  595. type : "CursorChange", action : "add", name:"rotatePointcloud"
  596. })
  597. viewer.dispatchEvent({
  598. type : "CursorChange", action : "remove", name:"movePointcloud"
  599. })
  600. }else if(cursor == 'translate'){
  601. viewer.dispatchEvent({
  602. type : "CursorChange", action : "add", name:"movePointcloud"
  603. })
  604. viewer.dispatchEvent({
  605. type : "CursorChange", action : "remove", name:"rotatePointcloud"
  606. })
  607. }else{
  608. viewer.dispatchEvent({
  609. type : "CursorChange", action : "remove", name:"movePointcloud"
  610. })
  611. viewer.dispatchEvent({
  612. type : "CursorChange", action : "remove", name:"rotatePointcloud"
  613. })
  614. }
  615. //this.cursorState = cursor
  616. }
  617. setLinkOperateState(name, state, informinformBy2d){
  618. if(state && name == this.operation || !state && name != this.operation)return
  619. let old = this.operation
  620. this.operation = state ? name : null
  621. if(this.operation == 'removeLink'){
  622. if(this.selectedLine){
  623. this.selectedLine.dispatchEvent('click')//删除
  624. }
  625. if(this.selectedPano && clickPanoToDisLink){
  626. this.selectedPano.circle.dispatchEvent('click')//删除
  627. }
  628. }
  629. if(this.operation != 'addLink'){
  630. this.linkGuideLine.visible = false
  631. }
  632. if(!state && !informinformBy2d){
  633. this.dispatchEvent({type: "operationCancel", operation: old})
  634. }
  635. if(this.operation == 'addLink'){
  636. viewer.dispatchEvent({type : "CursorChange", action : "add", name:"connectPano"} )
  637. }else{
  638. viewer.dispatchEvent({type : "CursorChange", action : "remove", name:"connectPano"} )
  639. }
  640. if(this.operation == 'removeLink'){
  641. viewer.dispatchEvent({type : "CursorChange", action : "add", name:"disconnectPano"} )
  642. }else{
  643. viewer.dispatchEvent({type : "CursorChange", action : "remove", name:"disconnectPano"} )
  644. }
  645. viewer.dispatchEvent('content_changed')
  646. }
  647. /////////////////////////////////
  648. initPanoLink(){
  649. images360.panos.forEach((pano)=>{
  650. this.panoLink[pano.id] = {}
  651. this.panoGroup.push([pano])
  652. })
  653. images360.panos.forEach((pano)=>{
  654. pano.visibles.forEach(index=>{//visibles中存的是下标!
  655. this.linkChange(pano, images360.getPano(index,'index'), 'add')
  656. })
  657. })
  658. //console.log('panoLink',this.panoLink)
  659. }
  660. linkChange(pano0, pano1, type){//修改link (type == 'remove'时,pano1可以为空)
  661. let temp = []
  662. if(type == 'add'){
  663. if(!pano1)return console.error('不支持add时pano1为空')
  664. this.panoLink[pano0.id][pano1.id] = this.panoLink[pano0.id][pano1.id] || {}
  665. this.panoLink[pano1.id][pano0.id] = this.panoLink[pano1.id][pano0.id] || {}
  666. }else{
  667. if(!pano1){
  668. for(let id in this.panoLink[pano0.id]){
  669. if(this.panoLink[pano0.id][id]){
  670. this.panoLink[id][pano0.id] = false
  671. temp.push(id)
  672. }
  673. }
  674. this.panoLink[pano0.id] = {} //全部断连
  675. }else{
  676. this.panoLink[pano0.id][pano1.id] = false
  677. this.panoLink[pano1.id][pano0.id] = false
  678. }
  679. }
  680. if(!pano1){ //全部断连
  681. temp.forEach(id=>{
  682. this.lineChange(pano0, images360.getPano(id) , type)
  683. })
  684. }else{
  685. this.lineChange(pano0, pano1, type)
  686. }
  687. this.groupChange(pano0, pano1, type)
  688. //this.updateSelectGroup()
  689. this.selectPano(this.selectedPano, false,true) //更新选中点云显示
  690. }
  691. lineChange(pano0, pano1, type){//修改line
  692. if(type == 'add'){
  693. if(this.panoLink[pano0.id][pano1.id].line) return
  694. let line = LineDraw.createFatLine([pano0.position, pano1.position], {mat:lineMats.default})
  695. line.name = `${pano0.id}-${pano1.id}`
  696. line.renderOrder = line.pickOrder = renderOrders.line
  697. this.lineMeshes.add(line)
  698. this.panoLink[pano0.id][pano1.id].line = this.panoLink[pano1.id][pano0.id].line = line
  699. line.addEventListener('mouseover', ()=>{
  700. if(this.clickToZoomInEnabled)return
  701. //if(this.activeViewName == 'mainView')return
  702. if(this.selectedLine != line)line.material = lineMats.hovered
  703. viewer.dispatchEvent({
  704. type : "CursorChange", action : "add", name:"hoverLine"
  705. });
  706. });
  707. line.addEventListener('mouseleave', ()=>{
  708. if(this.clickToZoomInEnabled)return
  709. //if(this.activeViewName == 'mainView')return
  710. if(this.selectedLine != line)line.material = lineMats.default
  711. viewer.dispatchEvent({
  712. type : "CursorChange", action : "remove", name:"hoverLine"
  713. });
  714. });
  715. line.addEventListener('click', (e)=>{
  716. if(this.clickToZoomInEnabled)return
  717. //if(this.activeViewName == 'mainView')return
  718. if(this.operation == 'removeLink'){
  719. if(this.selectedLine == line) this.selectLine(null)
  720. return this.linkChange(pano0, pano1, 'remove')
  721. }
  722. this.selectLine(line)
  723. })
  724. }else{
  725. let line = this.lineMeshes.children.find(e=>e.name == `${pano0.id}-${pano1.id}` || e.name == `${pano1.id}-${pano0.id}` )
  726. if(line){
  727. this.lineMeshes.remove(line)
  728. line.geometry.dispose()
  729. }
  730. }
  731. }
  732. groupChange(pano0, pano1, type){//修改group (type == 'remove'时,pano1可以为空)
  733. if(type == 'add'){
  734. Common.pushToGroupAuto([pano0, pano1], this.panoGroup )
  735. }else{
  736. let atGroup = this.panoGroup.find(e=>e.includes(pano0) && (e.includes(pano1) || !pano1));//所在组
  737. if(!atGroup){
  738. if(pano1){
  739. console.log('这两个pano原本就不在一个组', pano0.id, pano1.id)
  740. }else{
  741. console.log('pano0不在任何组', pano0)
  742. }
  743. return
  744. }
  745. //断开连接时,因为组内没有其他成员的连接信息,所以需要清除整组,并将剩余的一个个重新连接
  746. this.panoGroup.splice(this.panoGroup.indexOf(atGroup),1) //删除
  747. atGroup.forEach(pano=>{//然后再重新生成这两个和组的关系,各自分组
  748. this.panoGroup.push([pano])
  749. for(let id in this.panoLink[pano.id]){
  750. if(this.panoLink[pano.id][id]){
  751. let pano_ = images360.getPano(id)
  752. Common.pushToGroupAuto([pano, pano_], this.panoGroup )
  753. }
  754. }
  755. })
  756. }
  757. }
  758. selectLine(line){
  759. if(this.selectedLine == line)return
  760. if(this.selectedLine){
  761. this.selectedLine.material = lineMats.default;
  762. }
  763. if(line){
  764. line.material = lineMats.selected
  765. }
  766. this.selectedLine = line
  767. }
  768. addPanoMesh(){
  769. let map = texLoader.load(Potree.resourcePath+'/textures/correct_n.png' )
  770. /* circleMats.default_normal = new THREE.MeshBasicMaterial({
  771. map,
  772. color: 0xffffff,
  773. transparent: true,
  774. depthTest: false,
  775. depthWrite: false,
  776. }) */
  777. window.circleMats = circleMats
  778. circleMats.default_normal = new DepthBasicMaterial({
  779. map,
  780. color: 0xffffff,
  781. transparent: true,
  782. useDepth:true,
  783. backColor: 0x33ffdd,
  784. occlusionDistance: 10,//变为backColor距离
  785. clipDistance : 5,//消失距离
  786. maxClipFactor: 0.8,
  787. maxOcclusionFactor: 0.8,
  788. })
  789. circleMats.default_rtk_on = circleMats.default_normal.clone();
  790. circleMats.default_rtk_on.map = texLoader.load(Potree.resourcePath+'/textures/rtk-y-n.png' )
  791. circleMats.default_rtk_off = circleMats.default_normal.clone();
  792. circleMats.default_rtk_off.map = texLoader.load(Potree.resourcePath+'/textures/rtk-f-n.png' )
  793. circleMats.selected_normal = circleMats.default_normal.clone();
  794. circleMats.selected_normal.map = texLoader.load(Potree.resourcePath+'/textures/correct_s.png' )
  795. circleMats.selected_normal.useDepth = false;
  796. circleMats.selected_rtk_on = circleMats.selected_normal.clone();
  797. circleMats.selected_rtk_on.map = texLoader.load(Potree.resourcePath+'/textures/rtk-y-s.png' )
  798. circleMats.selected_rtk_off = circleMats.selected_normal.clone();
  799. circleMats.selected_rtk_off.map = texLoader.load(Potree.resourcePath+'/textures/rtk-f-s.png' )
  800. circleMats.hovered_normal = circleMats.default_normal.clone();
  801. circleMats.hovered_normal.color.set(0x00ff00)
  802. circleMats.hovered_normal.useDepth = false
  803. circleMats.hovered_rtk_on = circleMats.default_rtk_on.clone();
  804. circleMats.hovered_rtk_on.color.set(0x00ff00)
  805. circleMats.hovered_rtk_on.useDepth = false
  806. circleMats.hovered_rtk_off = circleMats.default_rtk_off.clone();
  807. circleMats.hovered_rtk_off.color.set(0x00ff00)
  808. circleMats.hovered_rtk_off.useDepth = false
  809. let setPos = (circle)=>{
  810. circle.position.copy(circle.pano.position)
  811. for(let id in this.panoLink[circle.pano.id]){
  812. let linkInfo = this.panoLink[circle.pano.id][id]
  813. if(linkInfo){
  814. LineDraw.updateLine(linkInfo.line, [circle.pano.position, images360.getPano(id).position] )
  815. }
  816. }
  817. circle.update() //update sprite Matrix
  818. }
  819. images360.panos.forEach(pano=>{
  820. var circle = new Sprite({mat: circleMats['default' + '_'+ this.getPanoRtkState(pano) ] , sizeInfo:{
  821. minSize : 50 , maxSize : 120, nearBound : 2, farBound : 10,
  822. },
  823. renderOrder : renderOrders.circle,
  824. pickOrder: renderOrders.circle
  825. })
  826. circle.pickDontCheckDis = true
  827. circle.name = 'panoCircle'
  828. circle.sid = pano.id
  829. circle.pano = pano;
  830. pano.circle = circle;
  831. this.panoMeshs.add(circle)
  832. setPos(circle)
  833. pano.addEventListener('rePos', setPos.bind(this,circle))
  834. let drag = ()=>{
  835. /* if(this.activeViewName == 'mainView' && this.tranMode == 'translate'){//如果3d页不禁止xy的话,这段打开
  836. this.transformControls.dispatchEvent('dragging')//触发拖拽
  837. return
  838. } */
  839. if(this.tranMode != 'translate' || this.activeViewName == 'mainView')return
  840. this.selectPano(circle.pano) //为了方便拖拽点云,拖动circle就直接选中
  841. viewer.inputHandler.drag.object = null //取消拖拽状态,否则不触发点云拖动
  842. }
  843. circle.addEventListener('drag', drag)
  844. circle.addEventListener('mouseover', ()=>{
  845. this.hoverPano(pano,true)
  846. })
  847. circle.addEventListener('mouseleave', ()=>{
  848. this.hoverPano(pano,false)
  849. })
  850. circle.addEventListener('click', ()=>{
  851. //if(this.activeViewName == 'mainView')return
  852. if(this.clickToZoomInEnabled)return
  853. if(clickPanoToDisLink && this.operation == 'removeLink'){
  854. this.linkChange(pano, null, 'remove') //删除所有连接
  855. }
  856. if(this.selectedPano == circle.pano) return this.selectPano(null)
  857. if(this.operation == 'addLink' && this.selectedPano){
  858. this.linkChange(this.selectedPano, circle.pano, 'add')
  859. //this.setLinkOperateState('addLink',false)
  860. return
  861. }
  862. //if(this.operation == 'removeLink' && this.selectedPano){ //和选择中心点冲突
  863. // this.linkChange(this.selectedPano, circle.pano, 'remove')
  864. // //this.setLinkOperateState('removeLink',false)
  865. // return
  866. // }
  867. this.selectPano(circle.pano)
  868. })
  869. })
  870. }
  871. hoverPano(pano, state){
  872. if(this.clickToZoomInEnabled)return
  873. if(pano && state){ //在hover一个pano之前,一定会先取消已经hover的pano, 最多存在一个hovered的pano
  874. if(this.hoveredPano == pano)return
  875. if(this.hoveredPano){
  876. this.hoverPano(this.hoveredPano,false)
  877. }
  878. this.hoveredPano = pano
  879. pano.hovered = true
  880. if(/* this.activeViewName == 'mainView' || */Alignment.handleState && this.selectedPano && this.selectedPano == pano)return
  881. if(this.operation != 'addLink' || !this.selectedPano || this.selectedPano == pano){ // this.selectedPano == pano?
  882. viewer.dispatchEvent({
  883. type : "CursorChange", action : "add", name:"hoverPano"
  884. });
  885. }
  886. if(this.selectedPano != pano) pano.circle.material = circleMats['hovered' + '_'+ this.getPanoRtkState(pano) ]
  887. }else if(pano && !state){//unhover
  888. if(this.hoveredPano != pano)return
  889. pano.hovered = false
  890. viewer.dispatchEvent({
  891. type : "CursorChange", action : "remove", name:"hoverPano"
  892. });
  893. if(this.selectedPano != pano) pano.circle.material = circleMats['default' + '_'+ this.getPanoRtkState(pano) ]
  894. this.hoveredPano = null;
  895. }else{//unhover any
  896. if(this.hoveredPano){
  897. this.hoverPano(this.hoveredPano, false)
  898. }
  899. }
  900. }
  901. selectPano(pano, informinformBy2d, force){
  902. if(this.selectedPano == pano && !force)return
  903. let lastSeletedPano = this.selectedPano
  904. let opaProp = this.activeViewName == 'top' ? opacitys.topView : opacitys.sideView
  905. if(this.selectedPano){
  906. this.selectedPano.circle.material = circleMats['default' + '_'+ this.getPanoRtkState(this.selectedPano) ]
  907. this.selectedPano.circle.renderOrder = renderOrders.circle
  908. this.selectedPano.removeEventListener('rePos',this.panoReposCallback)
  909. if(this.activeViewName == 'mainView'){
  910. }else{
  911. this.selectedClouds.forEach(e=>{
  912. e.changePointOpacity(opaProp.default,true)
  913. e.material.color = pointColor.default;
  914. })
  915. }
  916. }
  917. this.selectedPano = pano || null
  918. this.updateSelectGroup();
  919. if(pano){
  920. this.selectedPano.circle.material = circleMats['selected' + '_'+ this.getPanoRtkState(this.selectedPano) ]
  921. this.selectedPano.circle.renderOrder = this.selectedPano.circle.pickOrder = renderOrders.circleSelected //侧视图能显示在最前
  922. viewer.controls.setTarget(this.selectedPano.position) //3d时绕其为中心转动
  923. this.selectedPano.addEventListener('rePos',this.panoReposCallback)
  924. if(this.activeViewName == 'mainView'){
  925. }else{
  926. this.selectedClouds.forEach(e=>{
  927. e.changePointOpacity(opaProp.selected,true)
  928. e.material.color = pointColor.selected;
  929. })
  930. }
  931. if(this.currentFloor != 'all'){//如果原本不是展示全部楼层的话,自动切换楼层
  932. let atFloor = SiteModel.entities.find(e=>e.buildType == 'floor' && e.panos.includes(pano))
  933. if(!atFloor){
  934. atFloor = 'all'
  935. }else{
  936. }
  937. this.gotoFloor(atFloor, false, 600 )
  938. }
  939. }else{
  940. viewer.controls.setTarget(null)
  941. }
  942. this.updateCursor()
  943. this.updateTranCtl()
  944. if(informinformBy2d){
  945. if(this.selectedPano){
  946. if(this.activeViewName == 'mainView'){ //平移,focus选中的pano
  947. let distance = this.lastDisToPano || 5;
  948. if(lastSeletedPano){
  949. distance = viewer.mainViewport.camera.position.distanceTo(lastSeletedPano.position)
  950. }
  951. viewer.focusOnObject({ position:this.selectedPano.position}, 'point', null, {distance })
  952. }else{
  953. this.orthoMoveFit(this.selectedPano.position, {}, 500)
  954. }
  955. }
  956. }else{
  957. this.dispatchEvent({type:'panoSelect', pano })
  958. }
  959. this.updateIntersectEnable()
  960. viewer.dispatchEvent('content_changed')
  961. }
  962. /* updatePointLevels(){
  963. if(this.pauseUpdateLevels)return
  964. let maxBudget = Potree.config.pointDensity.panoEdit.pointBudget
  965. let visiCount1 = viewer.scene.pointclouds.filter(e=>e.visible).length
  966. let visiCount2 = viewer.scene.pointclouds.filter(e=>e.visibleNodes.length>0).length //屏幕范围内可见的个数
  967. let maxCount = 200, minCount = 1, minPer = 0.45 , maxPer = 1
  968. let percent1 = maxPer - ( maxPer - minPer) * THREE.Math.clamp((visiCount1 - minCount) / (maxCount - minCount),0,1)
  969. let percent2 = maxPer - ( maxPer - minPer) * THREE.Math.clamp((visiCount2 - minCount) / (maxCount - minCount),0,1)
  970. let percent = percent1*percent2
  971. if(this.activeViewName == 'mainView' ){
  972. //假设每个pointcloud所带的点个数大致相同,那么当可见点云个数越多,所能展示的level越低,否则因总个数超过budget的话密度会参差不齐。
  973. //pointcloud.changePointSize()
  974. //console.log('updatePointLevels', percent, visiCount)
  975. Potree.settings.UserDensityPercent = Math.sqrt(percent2)
  976. viewer.setPointBudget(maxBudget * percent2)
  977. }else{
  978. Potree.settings.UserDensityPercent = 1
  979. viewer.setPointBudget(maxBudget * percent)
  980. }
  981. viewer.setPointBudget(maxBudget * percent)
  982. viewer.setPointLevels()
  983. //侧面容易卡顿,但和显示的点数无关,似乎是因加载点云多而卡?为何正面不会
  984. //console.warn('setPointBudget', Potree.pointBudget, visiCount1,visiCount2, Potree.settings.UserDensityPercent)
  985. } */
  986. getPanoRtkState(pano){
  987. return pano.panosData.has_rtk ? pano.rtkState ? 'rtk_on' : 'rtk_off' : 'normal'
  988. }
  989. setPanoRtkState(pano,state){
  990. pano.rtkState = state
  991. pano.circle.material = circleMats[(this.selectedPano == pano ? 'selected' : 'default') + '_'+ this.getPanoRtkState(pano) ]
  992. }
  993. updateSelectGroup(){//更新选中的组
  994. this.selectedGroup = this.panoGroup.find(e=>e.includes(this.selectedPano))
  995. if(this.selectedGroup){
  996. this.selectedGroup = [this.selectedPano, ...this.selectedGroup.filter(e=>e != this.selectedPano)];//将选中的放第一个,便于旋转时绕其旋转。
  997. }
  998. //this.selectedClouds = this.selectedPano ? (this.selectedGroup || [this.selectedPano]).map(e=>e.pointcloud) : []
  999. this.selectedClouds = this.selectedPano ? this.selectedGroup.map(e=>e.pointcloud) : []
  1000. }
  1001. checkIfCanSave(){//如果未全部相连,不能保存
  1002. for(let datasetId in Potree.settings.datasetsPanos ) {
  1003. if(!this.checkIfAllLinked({datasetId})){
  1004. console.log('没有全部连通,不能保存。其中一个:', datasetId)
  1005. return
  1006. }
  1007. }
  1008. return true
  1009. }
  1010. checkIfAllLinked(o){//某个(or组所在的)数据集是否全部连通
  1011. let datasetId, group
  1012. if(o.group){
  1013. group = o.group
  1014. let pano = o.group[0]
  1015. if(!pano)return //会有没有漫游点的点云来编辑吗
  1016. datasetId = pano.pointcloud.dataset_id
  1017. }else if(o.datasetId){
  1018. datasetId = o.datasetId
  1019. group = this.panoGroup.find(panos=>panos[0].pointcloud.dataset_id == datasetId )
  1020. if(!group)return //要找的数据集的pano全部都孤立了
  1021. }
  1022. if(datasetId == void 0)return
  1023. let panos = Potree.settings.datasetsPanos[datasetId].panos
  1024. return panos.length == group.length
  1025. }
  1026. getSuggestLinkPanos(){//给出建议连接的点
  1027. let panos = [];
  1028. let startTime = Date.now()
  1029. for(let datasetId in Potree.settings.datasetsPanos ) {
  1030. if(!this.checkIfAllLinked({datasetId})){
  1031. let groups = this.panoGroup.filter(panos=>panos[0].pointcloud.dataset_id == datasetId )
  1032. groups = groups.sort((a,b)=>{return b.length - a.length})//找出个数最多的一组来连接其他组
  1033. let mainGroup = groups[0].slice(), subGroup;
  1034. for(let i=1,len=groups.length;i<len;i++){
  1035. subGroup = groups[i]
  1036. let minDis = {dis:Infinity, panos:[]}
  1037. for(let a=0, len1=mainGroup.length; a<len1; a++){
  1038. for(let b=0, len2=subGroup.length; b<len2; b++){
  1039. let dis = mainGroup[a].position.distanceToSquared(subGroup[b].position)
  1040. if(dis<minDis.dis){
  1041. minDis.dis = dis; minDis.panos = [mainGroup[a], subGroup[b]]
  1042. }
  1043. }
  1044. }
  1045. panos.push(minDis.panos)
  1046. //console.log('第i次',minDis)
  1047. mainGroup.push(...subGroup)//连接后,加入集合
  1048. }
  1049. }
  1050. }
  1051. //console.log('cost', Date.now() - startTime)
  1052. return panos
  1053. }
  1054. showSuggestLinkPanos(){
  1055. let groups = this.getSuggestLinkPanos()
  1056. let s = 0.7
  1057. let createCircle = (pano)=>{
  1058. let circle = new THREE.Mesh(pano.circle.geometry, suggestCircleMat)
  1059. circle.name = 'suggest-circle'
  1060. circle.scale.set(s,s,s)
  1061. circle.renderOrder = 100
  1062. pano.circle.add(circle)
  1063. }
  1064. groups.forEach(panos=>{
  1065. createCircle(panos[0])
  1066. createCircle(panos[1])
  1067. let line = LineDraw.createFatLine([panos[0].position, panos[1].position], {mat: lineMats.suggestLink})
  1068. this.suggestLines.push(line)
  1069. line.renderOrder = renderOrders.line
  1070. viewer.scene.scene.add(line)
  1071. })
  1072. }
  1073. getPanoPose(pano){
  1074. let pose = {
  1075. position: pano.position.clone(),
  1076. quaternion: new THREE.Quaternion().setFromRotationMatrix(pano.panoMatrix).premultiply(rotQua) ,
  1077. }
  1078. return pose
  1079. }
  1080. exportSavingData(){//输出漫游点新的坐标和朝向、以及连接信息
  1081. let sweepLocations = {}
  1082. for(let datasetId in Potree.settings.datasetsPanos ) {
  1083. let {panos} = Potree.settings.datasetsPanos[datasetId]
  1084. let data = panos.map(pano=>{
  1085. let visibles = []
  1086. for(let id in this.panoLink[pano.id]){
  1087. if(this.panoLink[pano.id][id]){
  1088. visibles.push(viewer.images360.getPano(id).index)
  1089. }
  1090. }
  1091. let {position, quaternion} = this.getPanoPose(pano);
  1092. return Object.assign({}, pano.panosData, {
  1093. uuid: pano.uuid,
  1094. /* pose:{
  1095. translation: dealData(pano.position.clone() ),
  1096. rotation: dealData(new THREE.Quaternion().setFromRotationMatrix(pano.panoMatrix).premultiply(rotQua) ),
  1097. }, */
  1098. pose : {
  1099. translation : dealData(position),
  1100. rotation : dealData(quaternion)
  1101. },
  1102. visibles,
  1103. use_rtk : !!pano.rtkState
  1104. //subgroup: 0,group: 1, "id_view":..
  1105. })
  1106. })
  1107. sweepLocations[datasetId] = {sweepLocations:data}
  1108. }
  1109. /* this.lineMeshes.children.forEach(e=>{//从line中搜集连接信息,而不从linkInfo,这样visibles不会重复一次
  1110. let names = e.name.split('-') //是不是该转成数字
  1111. var pano0 = names[0]
  1112. var pano1 = names[1]
  1113. sweepLocations.find(s=>s.uuid == pano0).visibles.push(pano1)
  1114. }) */
  1115. function dealData(value){
  1116. let v = math.toPrecision(value, 6)
  1117. if(v instanceof THREE.Quaternion){
  1118. return {x:v.x, y:v.y, z:v.z, w:v.w}
  1119. }else if(v instanceof THREE.Vector3){
  1120. return {x:v.x, y:v.y, z:v.z}
  1121. }
  1122. }
  1123. //console.log(sweepLocations)
  1124. return sweepLocations
  1125. }
  1126. }
  1127. /*
  1128. 不同数据集之间不能连线
  1129. 不同楼层可能也不能
  1130. 如果楼层在不同建筑物怎么办? 楼层切换按钮只能在一个建筑内切换。
  1131. 全部相连时不能移动和旋转
  1132. 如果未全部相连,不能保存
  1133. */
  1134. export default new PanoEditor()