ConvertViews.js 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245
  1. import math from './math.js'
  2. let bimViewer
  3. export default class ConvertViews extends THREE.EventDispatcher{
  4. constructor(isMobile ) {
  5. super()
  6. this.settings = {
  7. durations : {flyToPano:1000, dolly:20, bimAniOrigin:1000},
  8. checkModeDelay : 1000,
  9. }
  10. this.convertInfo //包含转换信息
  11. this.isMobile = isMobile
  12. }
  13. bindWithSameFakeType(sourceFakeApp, targetApp){//和另一个虚拟场景数据相配。 用于手机未分屏,切换场景;或者pc和bim对比时切换场景。
  14. let sourceApp = {sceneType: sourceFakeApp.sceneType, fakeApp:sourceFakeApp} //上一个场景
  15. this.createFakeApp(targetApp, true)//当前场景
  16. sourceApp.sceneName = 'sourceApp'
  17. targetApp.sceneName = 'targetApp'
  18. let convertInfo = this.computeAveDiffLon(sourceFakeApp, targetApp.fakeApp)
  19. if(sourceApp.sceneType == 'laser'){
  20. let data = this.computeShift({sourceApp,targetApp, convertInfo}) //因为有点云模式自由移动所以需要计算
  21. }
  22. //this.syncPosRot(sourceFakeApp.viewInfo, targetApp , convertInfo) //修改好位置朝向 这个4dkk的会报错但上一版是用这句
  23. if(sourceApp.sceneType == 'kankan' || sourceFakeApp.viewInfo.isAtPano){
  24. this.flyToPano(targetApp, sourceFakeApp.viewInfo.currentPano, null, {duration:0, zoomLevel:sourceFakeApp.viewInfo.zoomLevel})
  25. }
  26. this.syncView(sourceApp, targetApp, convertInfo)//这个不记得有什么bug了
  27. if(sourceApp.sceneType == 'laser'){
  28. targetApp.viewer.mainViewport.view.applyToCamera(targetApp.viewer.mainViewport.camera)//使获得的cameraInfo正确
  29. }else if(sourceApp.sceneType == 'kankan'){
  30. targetApp.app.core.get('Player').cameraControls.activeControl.locked = false //怎么刚加载时lock了
  31. targetApp.app.core.get('Player').update()//cameraControls.activeControl.update() //使获得的cameraInfo正确
  32. }
  33. return convertInfo
  34. }
  35. bindWithSameType(sourceApp,targetApp, isSwitchScene){ //左右分屏 同类型
  36. console.log('bindWithSameType')
  37. let reverse = isSwitchScene == 'source' //如果是左屏被换,则是左屏要跟右屏同步
  38. let master = reverse ? targetApp : sourceApp
  39. let customer = reverse ? sourceApp : targetApp
  40. this.createFakeApp(master,true)
  41. this.convertInfo = this.bindWithSameFakeType(master.fakeApp, customer) //先同步第一个画面
  42. //-------------------
  43. this.sourceApp = sourceApp
  44. this.targetApp = targetApp
  45. sourceApp.sceneName = 'sourceApp'
  46. targetApp.sceneName = 'targetApp'
  47. //后续的同步
  48. if(sourceApp.sceneType == 'laser'){
  49. //只监听左边
  50. let displayMode = (e)=>{
  51. targetApp.Potree.settings.displayMode = e.mode
  52. }
  53. sourceApp.viewer.images360.addEventListener('endChangeMode', displayMode)
  54. let dispose = ()=>{
  55. if(!sourceApp.viewer || !sourceApp.viewer.images360)return
  56. sourceApp.viewer.images360.removeEventListener('endChangeMode', displayMode)
  57. this.removeEventListener('clearBind-sameType',dispose)
  58. }
  59. this.addEventListener('clearBind-sameType',dispose)
  60. /*targetApp.viewer.images360.addEventListener('requestMode', (e)=>{
  61. console.error('requestMode targetApp', targetApp.name, e.mode)
  62. })
  63. targetApp.viewer.images360.addEventListener('endChangeMode', (e)=>{
  64. console.error('endChangeMode targetApp', targetApp.name, e.mode)
  65. })*/
  66. }else if(sourceApp.sceneType == 'kankan'){
  67. //暂时关闭快速过渡,因为跟不上
  68. sourceApp.app.core.get('Player').setPanoTaskEnable(false)
  69. targetApp.app.core.get('Player').setPanoTaskEnable(false)
  70. }
  71. let bind = (master, customer)=>{ //相互都能带动对方
  72. if(sourceApp.sceneType == 'laser'){
  73. var flyToPano = (e)=>{//同步点位
  74. if(master != this.masterApp )return
  75. let pano = customer.viewer.images360.getPano(e.toPano.pano.id)
  76. if(!pano)return console.error('找不到该e.panoId', e.toPano.pano.id)
  77. customer.viewer.images360.flyToPano({pano} )
  78. }
  79. master.viewer.images360.addEventListener('flyToPano',flyToPano)
  80. var cancelFlyToPano = (e)=>{//防止点云模式下飞到pano途中停止后另一边还在飞
  81. e.disturb && this.laserCancelFly(customer)
  82. }
  83. master.viewer.images360.addEventListener('flyToPanoDone',cancelFlyToPano)
  84. var cameraMove = (e)=>{
  85. if(master != this.masterApp || !customer.viewer )return
  86. this.fakeAppUpdateInfo(master)
  87. master.fakeApp.viewInfo.quaternionChanged = e.changeInfo && e.changeInfo.quaternionChanged
  88. this.syncView(master, customer)
  89. }
  90. master.viewer.addEventListener('camera_changed',cameraMove)
  91. var dragEnd = (e)=>{
  92. if(customer.viewer.inputHandler.drag){
  93. customer.viewer.inputHandler.onMouseUp(e) //从一侧拖拽到另一侧松开时,需要执行原先一侧的mouseup
  94. }
  95. }
  96. master.addEventListener('mouseup',dragEnd)
  97. var pointDensityChanged = ()=>{
  98. if(customer.Potree.settings.UserDensityPercent != master.Potree.settings.UserDensityPercent){
  99. customer.Potree.settings.UserDensityPercent = master.Potree.settings.UserDensityPercent //在sdk里初始化了UserDensityPercent所以不能只用UserPointDensity了
  100. customer.viewer.setPointLevels()
  101. console.log('UserPointDensity', master.sceneName, master.Potree.settings.UserDensityPercent)
  102. }
  103. }
  104. master.viewer.addEventListener('densityChange',pointDensityChanged)
  105. }else if(sourceApp.sceneType == 'kankan'){
  106. var player1 = master.app.core.get('Player')
  107. var player2 = customer.app.core.get('Player')
  108. let this_ = this
  109. var flyToPano = (e)=>{//同步点位
  110. if(master != this_.masterApp )return
  111. let pano = player2.model.panos.index[e.panoId]
  112. if(!pano)return console.error('找不到该e.panoId',e.panoId)
  113. player2.flyToPano({pano} )
  114. }
  115. player1.on("flying.started",flyToPano)
  116. var cameraMove = (e)=>{//暂时只有漫游模式
  117. if(!e.hasChanged.cameraChanged || !customer.app || !customer.app.core||
  118. master != this_.masterApp
  119. )return
  120. //console.log('cameraMove', master.sceneName)
  121. this.fakeAppUpdateInfo(master)
  122. this.syncView(master, customer)
  123. }
  124. player1.on("update",cameraMove)
  125. }
  126. let changeMaster = ()=>{
  127. this.masterApp = master //主控方。只有主控方能控制被控方。鼠标操作过mousedown mousewheel等才能认定为主控方
  128. }
  129. let dom = sourceApp.sceneType == 'laser' ? master.viewer.inputHandler.domElement : master.app.core.get('Player').domElement
  130. dom.addEventListener('pointerdown',changeMaster )
  131. dom.addEventListener('mousewheel',changeMaster )
  132. let dispose = ()=>{
  133. if(master.sceneType == 'laser'){
  134. if(!master.viewer )return //master已替换,不用处理
  135. master.viewer.images360.removeEventListener('flyToPano',flyToPano)
  136. master.viewer.images360.removeEventListener('flyToPanoDone',cancelFlyToPano)
  137. master.viewer.removeEventListener('camera_changed',cameraMove)
  138. master.viewer.removeEventListener('densityChange',pointDensityChanged)
  139. }else if(master.sceneType == 'kankan'){
  140. player1.off("flying.started",flyToPano)
  141. player1.off("update",cameraMove)
  142. }
  143. dom.removeEventListener('pointerdown',changeMaster)
  144. dom.removeEventListener('mousewheel',changeMaster)
  145. master.removeEventListener('mouseup',dragEnd)
  146. this.removeEventListener('clearBind-sameType',dispose)
  147. }
  148. this.addEventListener('clearBind-sameType',dispose)
  149. }
  150. bind(sourceApp, targetApp)
  151. bind(targetApp, sourceApp)
  152. sourceApp.sceneType == 'laser' && master.viewer.dispatchEvent('densityChange')//同步点云质量
  153. this.loaded = true
  154. }
  155. bindFakeWithBim(sourceFakeApp, targetApp, panoData ){// bim和其他类型互转(mobile), bim不一定是target
  156. if(targetApp.sceneType == 'bim'){
  157. bimViewer = targetApp.viewer
  158. bimViewer.getViewer().setTransitionAnimationState(false)
  159. targetApp.CLOUD.GlobalData.WalkRotationSpeed = -0.2 //反向一下
  160. }
  161. if(sourceFakeApp.sceneType == 'bim' && targetApp.sceneType == 'bim' ){
  162. console.log('还是bim')
  163. this.syncPosRot(sourceFakeApp.viewInfo, targetApp )
  164. return;
  165. }
  166. if(!panoData)return
  167. let sourceApp = {sceneType: sourceFakeApp.sceneType, fakeApp:sourceFakeApp}
  168. this.createFakeApp(targetApp)
  169. let {sourcePano, targetPano} = this.bimGetPanoData(sourceApp, targetApp, panoData)
  170. let convertAxis = sourceApp.sceneType == 'kankan' ? 'YupToZup' : targetApp.sceneType == 'kankan' ? 'ZupToYup' : null
  171. let convertInfo = {convertAxis}
  172. this.computeShift({sourcePano, targetPano, convertInfo})
  173. //console.log('convertInfo', convertInfo, sourcePano, targetPano)
  174. let selectBestPose = ()=>{
  175. let data = this.getTranPosData(sourceFakeApp.viewInfo, convertInfo, convertInfo.targetFakeApp == targetApp.fakeApp)
  176. let panos = targetApp.fakeApp.panos;
  177. let panos2 = panos.sort((a,b)=>{
  178. return data.position.distanceToSquared(a.position) - data.position.distanceToSquared(b.position)
  179. })
  180. let dir = new THREE.Vector3().subVectors( data.target, data.position )
  181. console.log('dir', dir)
  182. let prop = { duration:0,}
  183. if(targetApp.sceneType == 'laser'){
  184. targetApp.viewer.mainViewport.view.direction = dir
  185. }else{
  186. let player = targetApp.app.core.get('Player')
  187. console.log('nearest:', panos2[0].id)
  188. prop.aimDuration = 0
  189. prop.lookAtPoint = new THREE.Vector3().addVectors(panos2[0].position, dir)
  190. }
  191. this.flyToPano(targetApp, panos2[0].id, null, prop)
  192. }
  193. if(targetApp.sceneType == 'bim' ){
  194. sourceFakeApp.viewInfo.fov = null; //暂不改变bim单屏的fov,因为bim变不回来
  195. this.syncPosRot(sourceFakeApp.viewInfo, targetApp, convertInfo)
  196. }else if(targetApp.sceneType == 'laser' ){
  197. selectBestPose() //刚好在点位上的话这句设置完就正确了
  198. let currFakeApp = targetApp.fakeApp
  199. setTimeout(()=>{ //刚开始总是showPointCloud (且稍后会自动飞到某点)所以需要延时
  200. if(targetApp.fakeApp != currFakeApp)return //已经加载别的场景
  201. this.laserCancelFly(targetApp)
  202. if(this.ifCanChangePos(targetApp)){//点云模式的话
  203. this.syncPosRot(sourceFakeApp.viewInfo, targetApp, convertInfo)
  204. }else{
  205. }
  206. },this.settings.checkModeDelay+10)
  207. }else{//bim -> 固定点位
  208. selectBestPose()
  209. }
  210. }
  211. bindWithBim(sourceApp, targetApp, panoData ) {
  212. //if (!this.player1.model.panos.list.length || !this.player2.model.panos.list.length) return
  213. if(this.loaded || !targetApp ) return
  214. let needBindEvent = !this.targetApp // 若targetApp存在表明targetApp的dom未换掉,事件还存在
  215. this.createFakeApp(sourceApp)
  216. this.createFakeApp(targetApp)
  217. let {sourcePano, targetPano} = this.bimGetPanoData(sourceApp, targetApp, panoData)
  218. this.sourceApp = sourceApp
  219. this.targetApp = targetApp
  220. bimViewer = this.bimViewer = targetApp.viewer
  221. let modelSize = new THREE.Vector3
  222. let modelBound = bimViewer.getViewer().modelManager.boundingBox
  223. modelBound.getSize(modelSize)
  224. bimViewer.setNavigationMode(targetApp.Glodon.Bimface.Viewer.NavigationMode3D.Walk)
  225. //bimViewer.setFlySpeedRate(THREE.MathUtils.clamp( modelSize.length() / 10, 1, 6)) //会被限制
  226. bimViewer.setFlySpeedRate(1.5) //方向键速度,保持较小匀速,方便细调。
  227. this.sourceDom = sourceApp.sceneType == 'laser' ? this.sourceApp.viewer.inputHandler.domElement : this.sourceApp.app.core.get('Player').domElement
  228. if(targetPano){
  229. bimViewer.getViewer().setTransitionAnimationState(false) //setCameraStatus瞬间变化相机 ,or setCameraAnimation?
  230. var convertAxis = sourceApp.sceneType == 'kankan' && targetApp.sceneType == 'bim' && 'YupToZup'// Y朝上需要转换
  231. this.convertInfo = this.computeShift({sourcePano, targetPano, convertInfo:{convertAxis}})
  232. this.lastCamStatus = bimViewer.getCameraStatus()
  233. bimViewer.addEventListener('Rendered', (e)=>{//反向改变左侧相机
  234. let info = bimViewer.getCameraStatus()
  235. let poseChanged = !math.closeTo(this.lastCamStatus.position, info.position)
  236. || !math.closeTo(this.lastCamStatus.target, info.target)
  237. || !math.closeTo(this.lastCamStatus.fov, info.fov)
  238. if(poseChanged){
  239. if(this.ifCanChangePos(this.sourceApp)){
  240. let data = this.getTranPosData(info, this.convertInfo, true )
  241. this.laserSyncView(this.sourceApp, data)
  242. this.lastCamStatus = info
  243. }
  244. }
  245. })
  246. if(needBindEvent){
  247. this.bimBindCamEvent()
  248. }else{//替换的左侧的,需要使左侧和右侧同步, 其实是左侧要和上一个左侧先同步,再让右侧和左侧同步
  249. this.bindWithSameFakeType(this.lastFakeApp, sourceApp)
  250. }
  251. {
  252. let cameraMove
  253. if(sourceApp.sceneType == 'laser'){
  254. cameraMove = e => {
  255. targetApp && this.syncPosRot(this.getCameraData(sourceApp))
  256. }
  257. sourceApp.viewer.addEventListener('camera_changed', cameraMove)
  258. }else if(sourceApp.sceneType == 'kankan'){
  259. var player = this.sourceApp.app.core.get('Player')
  260. //this.sourceDom = player.domElement
  261. cameraMove = (e)=>{//暂时只有漫游模式
  262. if(!e.hasChanged.cameraChanged2)return
  263. //console.log('cameraMove', this.getCameraData(sourceApp))
  264. this.syncPosRot(this.getCameraData(sourceApp))
  265. }
  266. player.on("update",cameraMove)
  267. }
  268. let dispose = ()=>{
  269. if(sourceApp.sceneType == 'laser'){
  270. //if(!sourceApp.viewer || !sourceApp.viewer.images360)return
  271. sourceApp.viewer.removeEventListener('camera_changed', cameraMove)
  272. }else{
  273. //if(!sourceApp.app || !sourceApp.app.core)return
  274. player.off("update",cameraMove)
  275. }
  276. this.removeEventListener('clearBind-sameType',dispose)
  277. }
  278. this.addEventListener('clearBind-sameType',dispose)
  279. }
  280. /* bimViewer.addEventListener(targetApp.Glodon.Bimface.Viewer.Viewer3DEvent.ViewAdded,
  281. ()=>{
  282. this.loaded = true
  283. if(this.firstData){
  284. this.syncPosRot(this.firstData)
  285. }
  286. }
  287. ) */
  288. let data = this.getCameraData(sourceApp)
  289. this.syncPosRot(data)
  290. this.loaded = true
  291. }else{
  292. //分屏 不同步(设置点位绑定页面)
  293. let data = this.getCameraData(sourceApp)
  294. let camera = bimViewer.getViewer().camera
  295. if(data.fov && camera.fov != data.fov){
  296. camera.fov = data.fov
  297. camera.updateProjectionMatrix()
  298. }
  299. //将第一人称control补充完:
  300. //scroll
  301. let baseSpeed = THREE.MathUtils.clamp( Math.sqrt(modelSize.length()) / 5, 0.3, 3) //在modelBound中时的速度
  302. //console.log('baseSpeed',baseSpeed)
  303. let dom = bimViewer.getDomElement();
  304. dom.addEventListener('mousewheel', e => { //原版滚轮不能缩放,自己加一个
  305. if(e.wheelDelta == 0)return //mac
  306. let info = bimViewer.getCameraStatus()
  307. let dis = modelBound.distanceToPoint(info.position)
  308. let speed = baseSpeed + dis / 6
  309. //console.log('speed', speed)
  310. this.bimFlyTo({forwardDis: e.wheelDelta > 0 ? speed : -speed, duration:this.settings.durations.dolly , minRadius : baseSpeed})
  311. })
  312. //右键pan
  313. let dragging , pointerDelta = new THREE.Vector2, pointerStart = new THREE.Vector2
  314. dom.addEventListener('mousedown', e => {
  315. if(e.button == 2){//右键
  316. dragging = true
  317. pointerStart.set(e.clientX, e.clientY)
  318. }
  319. })
  320. dom.addEventListener('mousemove', e => {
  321. if(!dragging)return
  322. let pointerEnd = new THREE.Vector2(e.clientX, e.clientY)
  323. pointerDelta.subVectors(pointerEnd, pointerStart)
  324. pointerStart.copy(pointerEnd)
  325. bimViewer.getViewer().cameraControl.pan(pointerDelta.x,pointerDelta.y)
  326. })
  327. let mouseupAt = (target,e)=>{//触发target的mouseup
  328. if(!e.view && !e.isTrusted)return //应该就是由mouseupAt发出的事件,不再复制
  329. let event = new MouseEvent('mouseup', {
  330. button : e.button, buttons:e.buttons
  331. })
  332. target.dispatchEvent(event)
  333. }
  334. targetApp.addEventListener('mouseup', e => {
  335. dragging = false
  336. //触发当前sourceDom的mouseup
  337. mouseupAt(this.sourceDom,e)
  338. })
  339. this.sourceDom.addEventListener('mouseup', e => {
  340. dragging = false
  341. //触发当前targetApp的mouseup
  342. mouseupAt(targetApp,e)
  343. })
  344. this.addEventListener('mouseupOutOfWin', e => {
  345. dragging = false
  346. //触发当前targetApp的mouseup
  347. mouseupAt(targetApp,e)
  348. })
  349. targetApp.CLOUD.GlobalData.WalkRotationSpeed = -0.2 //反向一下
  350. //bimViewer.viewer.getViewer().editorManager.userInputEditor.enable = true//这句近似将control切换成orbit
  351. }
  352. }
  353. computeAveDiffLon(sourceFakeApp, targetFakeApp) {
  354. //获取两个场景的lon偏差值
  355. //需要点的个数>1, 且两个场景点一一对应,位置接近且顺序一致
  356. let diffLonAve = 0, length, diffLon,
  357. diffLons = []
  358. let panoPos1 = sourceFakeApp.panos.map(e=>{
  359. return e.position
  360. })
  361. let panoPos2 = targetFakeApp.panos.map(e=>{
  362. return e.position
  363. })
  364. if(panoPos1.length!=panoPos2.length){
  365. console.error('两个场景漫游点数量不同!',panoPos1,panoPos2)
  366. }
  367. length = Math.min(panoPos1.length, panoPos2.length )
  368. if(length<2){
  369. console.error('最小漫游点个数少于两个!!!')
  370. }
  371. if(length==0){//slam
  372. return {
  373. diffLon:0, //diffLonAve,
  374. diffQua: new THREE.Quaternion ,
  375. diffQuaInvert: new THREE.Quaternion,
  376. sourceFakeApp,
  377. targetFakeApp
  378. }
  379. }
  380. //挑选连续的两个点为向量来计算,如有123个漫游点,则选取12 23 31作为向量
  381. let index = 0
  382. while (index < length) {
  383. let pos11 = new THREE.Vector3().copy(panoPos1[index])
  384. let pos12 = new THREE.Vector3().copy(panoPos1[(index + 1) % length])
  385. let pos21 = new THREE.Vector3().copy(panoPos2[index])
  386. let pos22 = new THREE.Vector3().copy(panoPos2[(index + 1) % length])
  387. let vec1 = new THREE.Vector3().subVectors(pos11, pos12)
  388. let vec2 = new THREE.Vector3().subVectors(pos21, pos22)
  389. if(sourceFakeApp.sceneType == "laser" ){
  390. vec1.setZ(0), vec2.setZ(0)
  391. }else{
  392. vec1.setY(0), vec2.setY(0)
  393. }
  394. let diffLon0 = math.getAngle(vec1, vec2, sourceFakeApp.sceneType == "laser" ? 'z' : 'y')
  395. diffLons.push(diffLon0)
  396. diffLonAve += diffLon0
  397. index++
  398. }
  399. console.log('diffLons', diffLons)
  400. diffLonAve /= length
  401. diffLons = diffLons.sort((a,b)=>{return a-b})
  402. if(length<=2){
  403. diffLon = diffLonAve
  404. }else{
  405. //只选中间的一部分(类似中位数),以去掉坏点
  406. let i=1/3, j=2/3; //起始和终止。选取中间的三分之一
  407. let midList = diffLons.slice(i*length,Math.ceil(j*length));
  408. let sum = midList.reduce((total,cur)=>{return total+cur},0);
  409. diffLon = sum / midList.length;
  410. }
  411. let upVec = sourceFakeApp.sceneType == "laser" ? new THREE.Vector3(0, 0, 1) : new THREE.Vector3(0, 1, 0) //左右两个场景类型一样。暂不会有laser和4dkankan同步的情况
  412. let diffQua = new THREE.Quaternion().setFromAxisAngle(upVec, diffLon)
  413. console.log('diffLonAve', diffLonAve, 'diffLon', diffLon)
  414. return {
  415. diffLon, //diffLonAve,
  416. diffQua ,
  417. diffQuaInvert : diffQua.clone().invert(),
  418. sourceFakeApp,
  419. targetFakeApp
  420. }
  421. }
  422. computeShift(o={} ) { //获取两个可自由移动的场景的旋转和位移偏差值
  423. //需要点的个数>1, 且两个场景点一一对应,位置接近且顺序一致
  424. let panoPos1, panoPos2, convertInfo = o.convertInfo || {}, center1, center2, matrix
  425. if(o.sourceApp && o.targetApp && o.sourceApp.sceneType == o.targetApp.sceneType){
  426. var angle = convertInfo.diffLon; //直接使用 更精准
  427. panoPos1 = o.sourceApp.fakeApp.panos.map(e=>{
  428. return e.position
  429. })
  430. panoPos2 = o.targetApp.fakeApp.panos.map(e=>{
  431. return e.position
  432. })
  433. convertInfo.sourceFakeApp = o.sourceApp.fakeApp
  434. convertInfo.targetFakeApp = o.targetApp.fakeApp
  435. }else{//bim
  436. panoPos1 = o.sourcePano.map(e=>e.position) //pick两个点来计算
  437. panoPos2 = o.targetPano.map(e=>e.position)
  438. if(convertInfo.convertAxis){
  439. panoPos1 = panoPos1.map(e=>math.convertVector[convertInfo.convertAxis](e))
  440. }
  441. var vec1 = new THREE.Vector3().subVectors(panoPos1[0], panoPos1[1]) //旧的向量
  442. var vec2 = new THREE.Vector3().subVectors(panoPos2[0], panoPos2[1])//新的向量
  443. var angle = math.getAngle(vec1, vec2, 'z')
  444. }
  445. let compute = (panoPos1,panoPos2)=>{
  446. //中心点
  447. center1 = panoPos1.reduce((t,c)=>{return t.add(c)},new THREE.Vector3())
  448. center2 = panoPos2.reduce((t,c)=>{return t.add(c)},new THREE.Vector3())
  449. center1.multiplyScalar(1/panoPos1.length)
  450. center2.multiplyScalar(1/panoPos2.length)
  451. //var scale = vec2.length()/vec1.length()
  452. //var scaleMatrix = new THREE.Matrix4().makeScale(scale,scale,scale) //默认为1, 但由于坐标暂时是自己采集的,所以结果会是第一个点附近比较正确,越远偏差越大
  453. var matrix = new THREE.Matrix4().setPosition(/* panoPos1[0] */center1.clone().negate())//先以点0为基准平移到000
  454. //matrix.premultiply(scaleMatrix)//再缩放
  455. var rotateMatrix = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0,0,1), angle );
  456. matrix.premultiply(rotateMatrix)//和旋转
  457. var moveBackMatrix = new THREE.Matrix4().setPosition(/* panoPos2[0] */center2)
  458. matrix.premultiply(moveBackMatrix)//再移动到realPosition的点0处
  459. return matrix
  460. }
  461. let length = Math.min(panoPos1.length, panoPos2.length)
  462. if(length == 0){
  463. convertInfo.convertMatrix = new THREE.Matrix4
  464. convertInfo.convertMatrixInvert = new THREE.Matrix4
  465. return convertInfo
  466. }
  467. matrix = compute(panoPos1,panoPos2)
  468. //检查是否重合。直接将matrix作用于pos1中,理想情况是会和pos2完全一样。
  469. if(length>2){
  470. let diffVecs = panoPos1.slice(0,length).map((e,i)=>{
  471. let newPos = e.clone().applyMatrix4(matrix) //旋转过后。
  472. return new THREE.Vector3().subVectors(newPos, panoPos2[i]) // 和pos1之间的偏差。越小越重合
  473. })
  474. let disDiffs = diffVecs.map(e=>e.length())
  475. let disDiffs2 = disDiffs.sort((a,b)=>{return a-b})
  476. let maxTolerance = 2 * disDiffs2[Math.round(diffVecs.length/3)] //最大值限制在1/3的两倍处。不用绝对数值的原因:主要考虑到万一拍摄间隔很大,那么最小的diff都可能很大,所以还是按比例划分吧。
  477. if(disDiffs2[1]>=maxTolerance){//至少有两个
  478. maxTolerance = disDiffs2[1]
  479. }
  480. console.log('difVecs',diffVecs, 'disDiffs2',disDiffs2,'maxTolerance',maxTolerance)
  481. //排除掉偏差大的坏点
  482. let panoPos1new = panoPos1.filter((p,i)=>{return disDiffs[i]<=maxTolerance})
  483. let panoPos2new = panoPos2.filter((p,i)=>{return disDiffs[i]<=maxTolerance})
  484. if(panoPos1new.length > 1 && panoPos2new.length > 1){ //用剩下的点再算一次
  485. matrix = compute(panoPos1new,panoPos2new)
  486. }
  487. }
  488. convertInfo.convertMatrix = matrix
  489. convertInfo.convertMatrixInvert = matrix.clone().invert()
  490. return convertInfo
  491. //return { convertMatrix: matrix, convertMatrixInvert:matrix.clone().invert(), convertAxis:o.convertAxis}
  492. /*
  493. 用于场景自由移动时。缺点:切换点云模式时,如果点位不准 偏差大,就会瞬移一下。
  494. 不过目前四维看看不支持到dollhouse
  495. */
  496. }
  497. syncPosRot(data, customer, convertInfo ){//同步 自由位置和朝向(不被漫游点束缚时)
  498. /*
  499. if(!this.loaded){
  500. return this.firstData = data
  501. } */
  502. convertInfo = convertInfo || this.convertInfo || {}
  503. let {position,target} = this.getTranPosData(data, convertInfo, customer && customer.fakeApp == convertInfo.sourceFakeApp )
  504. if(customer && customer.sceneType == 'laser'){
  505. this.laserSyncView(customer, {position,target})
  506. }else if(customer && customer.sceneType == 'kankan'){
  507. this.syncView(sourceApp, targetApp, convertInfo)
  508. }else{
  509. let info = bimViewer.getCameraStatus()
  510. let msg = {
  511. position,
  512. target,
  513. up:info.up,
  514. //前三个缺一不可
  515. fov: data.fov || info.fov, //fov 用setCameraStatus 无效
  516. }
  517. bimViewer.setCameraStatus(msg)
  518. this.lastCamStatus = msg //记录下来,防止反向传输
  519. let camera = bimViewer.getViewer().camera
  520. if(data.fov && camera.fov != data.fov){
  521. camera.fov = data.fov
  522. camera.updateProjectionMatrix()
  523. }
  524. }
  525. }
  526. ifCanChangePos(app){
  527. return app.sceneType == 'laser' && app.Potree.settings.displayMode != 'showPanos' //app.fakeApp.viewInfo.displayMode != 'showPanos'
  528. }
  529. /*
  530. laser暂时做成这样: 全景模式时不跟踪pos,跟踪pano变化。点云模式时也跟踪pano变化,但移动时完全跟踪位置变化 ,所以会有左边marker在脚下,右边marker不在脚下的情况。
  531. */
  532. bimGetPanoData(sourceApp, targetApp, panoData){
  533. if(panoData){
  534. let sourcePano,targetPano
  535. let pano1 = [//bim
  536. {position:new THREE.Vector3().copy(panoData.p1.pos2 || panoData.p1.position)},
  537. {position:new THREE.Vector3().copy(panoData.p2.pos2 || panoData.p2.position)}
  538. ]
  539. let getPano2 = (app)=>{
  540. if(panoData.p1.id != void 0){//老数据用的id, 因为slam场景无漫游点所以改为用pos
  541. return [
  542. app.fakeApp.panos.find(e=>e.sid == panoData.p1.id || e.id == panoData.p1.id),
  543. app.fakeApp.panos.find(e=>e.sid == panoData.p2.id || e.id == panoData.p2.id)
  544. ]
  545. }else{
  546. return [
  547. {position:new THREE.Vector3().copy(panoData.p1.pos1)},
  548. {position:new THREE.Vector3().copy(panoData.p2.pos1)}
  549. ]
  550. }
  551. }
  552. if(targetApp.sceneType == 'bim'){
  553. targetPano = pano1
  554. sourcePano = getPano2(sourceApp)
  555. }else{
  556. targetPano = getPano2(targetApp)
  557. sourcePano = pano1
  558. }
  559. if( !sourcePano[0] || !sourcePano[1] || !targetPano[0] || !targetPano[1] ){
  560. console.error('缺少绑定漫游点,请重新绑定', sourcePano, targetPano )
  561. }//可能原因:漫游点改变
  562. return {sourcePano, targetPano}
  563. }else return {}
  564. }
  565. getTranPosData(data, convertInfo={}, ifRevert ){//根据convertInfo获得转换的数据
  566. let position = new THREE.Vector3, target = new THREE.Vector3
  567. if(data.position){
  568. position = new THREE.Vector3().copy(data.position)
  569. }
  570. if(!data.target){
  571. if(data.quaternion){
  572. let dir = new THREE.Vector3(0, 0, -1).applyQuaternion(data.quaternion)
  573. target.copy(position).add(dir)
  574. }
  575. }else{
  576. target.copy(data.target)
  577. }
  578. if(convertInfo.convertMatrix){
  579. if(ifRevert){
  580. position.applyMatrix4(convertInfo.convertMatrixInvert)
  581. target.applyMatrix4(convertInfo.convertMatrixInvert)
  582. if(convertInfo.convertAxis){
  583. position = math.convertVector[convertInfo.convertAxis](position)
  584. target = math.convertVector[convertInfo.convertAxis](target)
  585. }
  586. }else{
  587. if(convertInfo.convertAxis){
  588. position = math.convertVector[convertInfo.convertAxis](position)
  589. target = math.convertVector[convertInfo.convertAxis](target)
  590. }
  591. position.applyMatrix4(convertInfo.convertMatrix)
  592. target.applyMatrix4(convertInfo.convertMatrix)
  593. }
  594. }
  595. return {position, target}
  596. }
  597. getCameraData(app){
  598. if(app.sceneType == 'laser'){
  599. let camera = app.viewer.mainViewport.camera
  600. return {
  601. position: camera.position.clone(),
  602. quaternion: camera.quaternion.clone(),
  603. fov: camera.fov,
  604. zoomLevel: app.viewer.images360.zoomLevel,
  605. }
  606. }else if(app.sceneType == 'kankan'){
  607. let player = app.app.core.get('Player')
  608. return {
  609. position: player.position.clone(),
  610. quaternion: player.quaternion.clone(),
  611. zoomLevel: player.zoomLevel,//fov: player.zoomFov,
  612. }
  613. }else{
  614. let bimViewer = app.viewer
  615. let info = bimViewer.getCameraStatus();
  616. return {
  617. position: info.position,
  618. target: info.target,
  619. fov: info.fov,
  620. }
  621. }
  622. }
  623. createFakeApp(app, addsubInfo){ //为每个app创建fakeApp, 里面包含了场景基本信息。
  624. if(!app.fakeApp){//不能重复建立,作为唯一标识
  625. let fakeApp = {
  626. isFake : true, //标志是虚拟的app。每个真实的app都要带一个这个。在移动端如果大的销毁了还有小的
  627. sceneType : app.sceneType,
  628. id : getId(),
  629. }
  630. if(app.sceneType != 'bim'){
  631. function getPanos(panos){ // only data
  632. return panos.map(e=>{return {id:e.id, sid:e.sid, position:e.position, quaternion:e.quaternion}})
  633. }
  634. fakeApp.panos = app.sceneType == 'laser' ? getPanos(app.viewer.images360.panos) : getPanos(app.app.core.get('Player').model.panos.list)
  635. }
  636. Object.defineProperty(app,'fakeApp',{
  637. value: fakeApp,
  638. Configurable : false, //不可替换和删除
  639. })
  640. }
  641. if(addsubInfo){
  642. this.fakeAppUpdateInfo(app)
  643. }
  644. return app.fakeApp
  645. }
  646. fakeAppUpdateInfo(app){ //更新表现信息
  647. let viewInfo
  648. let cameraData = this.getCameraData(app)
  649. if(app.sceneType == 'laser'){
  650. let images360 = app.viewer.images360
  651. viewInfo = {
  652. displayMode : app.Potree.settings.displayMode,
  653. currentPano : images360.currentPano && images360.currentPano.id,
  654. isAtPano : images360.isAtPano(),
  655. quaternionChanged : true,
  656. bumping: images360.bumping,
  657. isFlyToPano: !!images360.latestToPano
  658. }
  659. }else if(app.sceneType == 'kankan'){
  660. let player = app.app.core.get('Player')
  661. viewInfo = {
  662. currentPano : player.currentPano.id,
  663. lon : player.cameraControls.activeControl.lon,
  664. lat : player.cameraControls.activeControl.lat,
  665. zoomLevel : player.zoomLevel,
  666. }
  667. }else{
  668. viewInfo = {}
  669. }
  670. for(let i in cameraData){
  671. viewInfo[i] = cameraData[i]
  672. }
  673. app.fakeApp.viewInfo = viewInfo
  674. }
  675. /* getPano(app){
  676. return app.sceneType == 'laser' ? app.viewer.images360.getPano(id) : app.app.core.get('Player').panos.index[id]
  677. } */
  678. syncView(master, customer, convertInfo ){//同类型的同步( 相当于moveCamera的函数 ),但不包括点位的同步
  679. let fakeApp = master.fakeApp;
  680. convertInfo = convertInfo || this.convertInfo
  681. if(fakeApp.sceneType == 'laser'){
  682. //customer.Potree.settings.displayMode = fakeApp.viewInfo.displayMode
  683. if(fakeApp.viewInfo.isAtPano || fakeApp.viewInfo.bumping || fakeApp.viewInfo.isFlyToPano || fakeApp.viewInfo.displayMode == 'showPanos'){ //不改变漫游点,仅转换朝向
  684. if( fakeApp.viewInfo.quaternionChanged){
  685. let diffQua = customer.fakeApp == convertInfo.targetFakeApp ? convertInfo.diffQua : convertInfo.diffQuaInvert
  686. //let diffQua = customer == this.targetApp ? convertInfo.diffQua : convertInfo.diffQuaInvert
  687. let quaternion = fakeApp.viewInfo.quaternion.clone().premultiply(diffQua)
  688. let rotation = new THREE.Euler().setFromQuaternion(quaternion)
  689. customer.viewer.mainViewport.view.rotation = rotation
  690. //console.log('cameraMove',customer == this.targetApp)
  691. }
  692. if(fakeApp.viewInfo.displayMode == 'showPanos' ){
  693. /* if(customer.viewer.mainViewport.camera.fov != fakeApp.viewInfo.fov){
  694. customer.viewer.mainViewport.camera.fov = fakeApp.viewInfo.fov
  695. customer.viewer.mainViewport.camera.updateProjectionMatrix()
  696. } */
  697. if(customer.viewer.images360.zoomLevel != fakeApp.viewInfo.zoomLevel){
  698. customer.viewer.images360.zoomTo(fakeApp.viewInfo.zoomLevel , !0)
  699. //customer.viewer.mainViewport.camera.updateProjectionMatrix()
  700. }
  701. customer.Potree.settings.zoom.max = Math.max(fakeApp.viewInfo.zoomLevel, customer.Potree.settings.zoom.max);//防止最大只有2
  702. }
  703. }else{//转换朝向和位置
  704. this.syncPosRot(fakeApp.viewInfo, customer , convertInfo)
  705. }
  706. }else if(fakeApp.sceneType == 'kankan'){
  707. let player = customer.app.core.get('Player')
  708. let diffLon = THREE.Math.radToDeg(customer == this.sourceApp ? -convertInfo.diffLon : convertInfo.diffLon)
  709. player.cameraControls.controls.panorama.lon = fakeApp.viewInfo.lon + diffLon
  710. player.cameraControls.controls.panorama.lat = fakeApp.viewInfo.lat
  711. if(player.zoomLevel != fakeApp.viewInfo.zoomLevel){
  712. player.zoomTo(fakeApp.viewInfo.zoomLevel)
  713. }
  714. }
  715. }
  716. laserSyncView(app,data){
  717. app.viewer.mainViewport.view.position.copy(data.position)
  718. app.viewer.mainViewport.view.lookAt(data.target)
  719. }
  720. laserCancelFly(app){//laser清除移动到下一个位置的动画
  721. app.viewer.images360.cancelFlyToPano()
  722. app.viewer.mainViewport.view.cancelFlying()
  723. }
  724. laserInit(app, mode){//加载完laser后立即初始化
  725. if(!app.viewer){
  726. return console.error('!app.viewer', app.viewer)
  727. }
  728. console.warn('laserInit', app.name, mode)
  729. this.laserMode = mode
  730. app.Potree.settings.displayMode = this.laserMode == 0 ? "showPanos" : "showPointCloud" //先修改否则一开始不一样后面位置同步不了
  731. app.viewer.images360.baseFov = app.Potree.config.view.fov //暂时加这一句,过后删除
  732. this.laserCancelFly(app)//app.viewer.images360.cancelFlyToPano()
  733. app.viewer.mainViewport.view.minPitch += 0.01 //防止bim垂直视角上的闪烁(似乎是因 up 要乘以某矩阵导致微小偏差所致)
  734. app.viewer.mainViewport.view.minPitch -= 0.01
  735. /* app.viewer.images360.panos.forEach(pano=>{
  736. app.viewer.updateVisible(pano.label2, 'notDisplay', true)
  737. pano.dispatchEvent({type:'changeMarkerTex',name:'ring'})
  738. }) */
  739. //app.Potree.settings.pointDensity = 'high'
  740. app.Potree.settings.UserDensityPercent = 1; //在sdk里初始化了UserDensityPercent所以不能只用UserPointDensity了
  741. app.viewer.setPointLevels()
  742. app.Potree.settings.rotAroundPoint = false //去除原因:比较好同步,尤其当左边在当前点位,右边同步后却离开当前点位的话拖拽就会绕点旋转了
  743. setTimeout(()=>{//laser的代码中莫名会请求showPointCloud,所以尽量晚一点覆盖它,再确保一次
  744. if(app.Potree){
  745. app.Potree.settings.displayMode = this.laserMode == 0 ? "showPanos" : "showPointCloud"
  746. }
  747. }, this.settings.checkModeDelay)
  748. }
  749. laserChangeMode(mode){//整个页面的mode是统一的
  750. this.laserMode = mode
  751. }
  752. bimFlyTo(data){//bim修改位置,可渐变
  753. let info = bimViewer.getCameraStatus()
  754. let vec = new THREE.Vector3().subVectors(info.target, info.position)
  755. let radius = 10//vec.length() //修改了target到position的距离会影响pan时的速度
  756. let dir = vec.clone().normalize()
  757. let position = data.position
  758. if(!position){
  759. position = new THREE.Vector3().addVectors(info.position, dir.clone().multiplyScalar(data.forwardDis))//forwardDis:前进距离
  760. radius = Math.max(radius-data.forwardDis, data.minRadius || 0.7)
  761. }
  762. if(data.duration != void 0){
  763. bimViewer.getViewer().animator.setDuration(data.duration)//滚轮缩放时长,原先:1000
  764. }
  765. //console.log('radius',radius)
  766. let target = new THREE.Vector3().addVectors(position, dir.clone().multiplyScalar(radius))
  767. let msg = {//不能修改
  768. position,
  769. target,
  770. up: info.up, // 不能是Vector3(0,0,1),否则俯视时会不受控制打转
  771. //前三个缺一不可
  772. }
  773. bimViewer.setCameraStatus(msg)
  774. }
  775. flyToPano(app, panoId, position, o={}){
  776. if(app.sceneType == 'laser'){
  777. this.laserCancelFly(app)//app.viewer.images360.cancelFlyToPano()
  778. if(app.viewer.images360.panos.length == 0){//slam
  779. app.viewer.mainViewport.view.setView({position, duration:1000})
  780. }else{
  781. app.viewer.images360.flyToPano(Object.assign({},{
  782. pano: app.viewer.images360.getPano(panoId, (panoId+'').includes('|')?'sid':'id')
  783. },o))
  784. }
  785. }else{
  786. let player = app.app.core.get('Player')
  787. player.flyToPano(Object.assign({},{
  788. pano: player.model.panos.index[panoId]
  789. },o))
  790. }
  791. }
  792. lockCamera(locked){//禁止操作改变相机
  793. this.locked = locked
  794. this.updateCtrlEnable()
  795. }
  796. setPanoMode(state){
  797. this.isPanoMode = state
  798. this.updateCtrlEnable()
  799. }
  800. updateCtrlEnable(){//是否禁止bim响应操作
  801. this.bimViewer.camera3D.enableRotate(this.locked ? false : true)
  802. this.bimViewer.enableShortcutKey((this.locked || this.isPanoMode) ? false : true) //键盘移动
  803. }
  804. bimBindCamEvent(){//bim的分屏 是由另一方的camera带动bim的camera,故需要将bim的鼠标事件传递到另一边
  805. this.lockCamera(true)
  806. /* this.targetApp.viewer.addEventListener(this.targetApp.Glodon.Bimface.Viewer.Viewer3DEvent.MouseClicked,(e)=>{
  807. console.log('MouseClicked',e)//
  808. }); */
  809. let dom1 = this.bimViewer.getDomElement()
  810. let getEvent = (type, e)=>{
  811. let clientWidth1 = this.sourceDom.clientWidth
  812. let clientHeight1 = this.sourceDom.clientHeight
  813. let clientWidth2 = dom1.clientWidth
  814. let clientHeight2 = dom1.clientHeight
  815. return new MouseEvent(type, {
  816. bubbles: false,//?
  817. cancelable: true,
  818. view: this.sourceApp,
  819. /* clientX: e.clientX,
  820. clientY: e.clientY, */
  821. clientX: clientWidth1 * e.clientX / clientWidth2 , //鼠标在右屏的比例的左屏的相同,针对右屏全屏等左右不对称的情况
  822. clientY: clientHeight1 * e.clientY / clientHeight2,
  823. button: e.button, buttons: e.buttons, which: e.which,
  824. altKey: e.altKey, ctrlKey: e.ctrlKey, shiftKey:e.shiftKey, metaKey: e.metaKey,
  825. detail:e.detail,
  826. //target : dom2
  827. });
  828. }
  829. //let pointerDownPos = new THREE.Vector2
  830. dom1.addEventListener('mousedown',(e)=>{
  831. let event = getEvent('mousedown', e)
  832. this.sourceApp && this.sourceDom.dispatchEvent(event)
  833. //pointerDownPos.set(e.clientX,e.clientY)
  834. })
  835. dom1.addEventListener('mousemove',(e)=>{
  836. let event = getEvent('mousemove', e)
  837. this.sourceApp && this.sourceDom.dispatchEvent(event)
  838. })
  839. dom1.addEventListener('mouseup',(e)=>{
  840. if(!this.sourceApp)return
  841. let event = getEvent('mouseup', e)
  842. event.unableClick = true //最好禁止右侧点击行走。否则和点击效果冲突
  843. if(this.sourceApp.sceneType == 'laser'){
  844. this.sourceApp.dispatchEvent(event) //mouseup 在laser中加在window上的
  845. }else{
  846. let player = this.sourceApp.app.core.get('Player')
  847. player.mouseCouldBeClickToMove = false //dont click
  848. this.sourceDom.dispatchEvent(event)
  849. }
  850. })
  851. dom1.addEventListener('mousewheel',(e)=>{
  852. let event = getEvent('mousewheel', e)
  853. event.wheelDelta = e.wheelDelta //wheelDelta没法在getEvent参数中赋值
  854. this.sourceApp && this.sourceDom.dispatchEvent(event)
  855. })
  856. let stop = (e)=>{ //drag到另一边时停止旋转, 防止转到另一边
  857. let event = getEvent('mouseup', e)
  858. this.sourceApp && this.sourceApp.dispatchEvent(event)
  859. }
  860. dom1.addEventListener('mouseout',stop)
  861. dom1.addEventListener('mouseover',stop)
  862. }
  863. clear(o={}){//加载新场景前清除一下
  864. this.loaded = false;
  865. if(o.dontClearTarget){
  866. if(this.sourceApp){
  867. this.lastFakeApp = this.sourceApp.fakeApp //记住当前左屏
  868. this.fakeAppUpdateInfo(this.sourceApp)
  869. }
  870. }else{
  871. this.targetApp = null
  872. this.lastFakeApp = null
  873. }
  874. this.sourceApp = null;
  875. this.dispatchEvent({type:'clearBind-sameType'})
  876. window.Log('clear done')
  877. }
  878. }
  879. let num = 0
  880. function getId(){
  881. return num++
  882. }
  883. /*
  884. note:
  885. 还不支持laser和4dkk同屏
  886. 访问:
  887. window.global__.sourceApp.fakeApp
  888. window.global__.targetApp.fakeApp
  889. window[1] 不准确,不一定是targetApp
  890. 旋转只能通过target设置, 不能直接改camera.quaternion
  891. 当且仅当发送方相机属性变化后才传递过来,就不在这里判断是否变化了。
  892. (所以只需要实时检测相机是否改变, hasChanged后发送)
  893. 如果角度同步有偏差,请查看
  894. computeAveDiffLon
  895. 如果位置同步有偏差,请查看
  896. computeShift,
  897. 添加label:
  898. window[1].viewer.images360.panos.forEach(e=>e.addLabel())
  899. */
  900. /*
  901. 其他代码:
  902. initTagAdd(){
  903. let markerConfig = new Bimface.Plugins.Marker3D.Marker3DContainerConfig();
  904. markerConfig.viewer = this.viewer;
  905. let tags = new Bimface.Plugins.Marker3D.Marker3DContainer(markerConfig);
  906. console.log('tags',tags)
  907. this.addEventListener('addTag',(e)=>{
  908. if(this.targetPano[e.index].tag){
  909. tags.removeItemById(this.targetPano[e.index].tag.id)
  910. }
  911. let position = new THREE.Vector3
  912. if(e.position){
  913. position.copy(e.position)
  914. }else{
  915. let currStatus = this.viewer.getCameraStatus()
  916. position.copy(currStatus.position)
  917. }
  918. let marker3dConfig = new Bimface.Plugins.Marker3D.Marker3DConfig();
  919. marker3dConfig.src = 'images/hotpoint'+ e.index +'.png'//"http://static.bimface.com/resources/3DMarker/warner/warner_red.png";
  920. marker3dConfig.worldPosition = new THREE.Vector3().copy(position)
  921. marker3dConfig.worldPosition.z -= belowHeight
  922. marker3dConfig.tooltip = '此为漫游点'+e.index //三维标签的提示
  923. let tag = new Bimface.Plugins.Marker3D.Marker3D(marker3dConfig);
  924. tags.addItem(tag);
  925. this.viewer.clearSelectedComponents();
  926. this.viewer.render();
  927. this.targetPano[e.index].tag = tag
  928. this.updatePanoMatch(position, e.index )
  929. })
  930. }
  931. this.viewer.addEventListener( Bimface.Viewer.Viewer3DEvent.MouseClicked, (objectData)=>{
  932. let position = objectData.worldPosition.clone().add({x:0,y:0,z:height});
  933. })
  934. addMesh(cameraData){
  935. var mesh = new Bimface.Plugins.Geometry.Plane({
  936. type:'rectangle', points:[{x:-0.1,y:-0.1,z:0},{x:0.1,y:0.1,z:0}]
  937. });
  938. var extObjMng = new Bimface.Plugins.ExternalObject.ExternalObjectManager(viewer2);
  939. extObjMng.loadObject({ name: 'plane', object: mesh});//作为外部构件添加到场景中
  940. //mesh.children[0].position.copy(cameraData.position).setZ(0.5)
  941. mesh.children[0].up.set(0,0,1)
  942. mesh.children[0].rotation.set(0,0,Math.PI/2)
  943. this.plane = mesh
  944. window.extObjMng = extObjMng
  945. }
  946. */