index.js 92 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266
  1. import mitt from 'mitt'
  2. import libTransform from 'coordtransform';
  3. import axios from 'axios' //{ axios } from '@/api'
  4. let requestLoadCount = 0
  5. let maxLoadingCount = 2; //正在加载模型的最大数目
  6. //0看看,1看见,2深时,3用户上传三维模型,4深时mesh,5深光点云,6深光mesh
  7. const ModelTypes = {
  8. 0 : {name:'看看(八目)', panos4dkk:true},
  9. 1 : {name:'看见(双目转台)', panos4dkk:true, rot90:true},
  10. 2 : {name:'深时', },
  11. 3 : {name:'用户上传三维模型'},
  12. 4 : {name:'深时mesh(激光转台)',panos4dkk:true, rot90:true},//3dtiles or obj
  13. 5 : {name:'深光点云' },
  14. 6 : {name:'深光mesh',panos4dkk:true, rot90:true},//3dtiles
  15. 7 : {name:'圆周率相机' },//圆周率相机场景
  16. }
  17. let getMapProp = function(type1, type2){
  18. let props
  19. if(type2 == 'amap'){
  20. props = type1 == 'satellite' ? [
  21. {url: `//wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=6&x={x}&y={y}&z={z}&layer=6&token=YOUR_API_KEY`, //style=6是卫星,7是标准
  22. maximumLevel: 18 ,
  23. name:'高德baseLayer'
  24. },{
  25. url: `//wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}&layer=6&token=YOUR_API_KEY`, //style=6是卫星,7是标准
  26. maximumLevel: 18,
  27. name:'高德textLayer'
  28. }] : [
  29. {url: `//wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}&layer=6&token=YOUR_API_KEY`, //style=6是卫星,7是标准
  30. maximumLevel: 19 ,
  31. name:'高德baseLayer'
  32. },
  33. ]
  34. }else{
  35. props = {
  36. url: 'https://mt0.google.com/vt/lyrs='+ (type1 == 'satellite'?"y":"m" ) +'&x={x}&y={y}&z={z}',
  37. maximumLevel: 22
  38. }
  39. }
  40. return props
  41. }
  42. let defaultMapProps = getMapProp('satellite','amap')
  43. [{url: `//wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=${satellite?6:7}&x={x}&y={y}&z={z}&layer=6&token=YOUR_API_KEY`, //style=6是卫星,7是标准
  44. maximumLevel: satellite?18:19,
  45. name:'高德baseLayer'
  46. },{
  47. url: `//wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}&layer=6&token=YOUR_API_KEY`, //style=6是卫星,7是标准
  48. maximumLevel: 18,
  49. name:'高德textLayer'
  50. }]
  51. let cesAspect , cesImageryProvider, mapProps = defaultMapProps
  52. const Id_noIntersect = -100 //path绘制在地图上的点,modelId传这个值,勿更改
  53. let isValidPoint = (modelId)=>{//所存的modelId没被删或者它本身不在模型上
  54. return modelId == Id_noIntersect || viewer.objs.children.concat(viewer.scene.pointclouds).some(e=>e.dataset_id == modelId )
  55. }
  56. let curSelectPath
  57. {
  58. // 84坐标转高德 (国外地区用84,所以地理注册时填的是84,我这需要转成高德)
  59. const wgs84ToAMap = (pos ) => {
  60. const latlng = libTransform.wgs84togcj02(pos.x, pos.y)
  61. return {
  62. x: latlng[0],
  63. y: latlng[1]
  64. }
  65. }
  66. // 高德坐标转84
  67. const aMapToWgs84 = (pos ) => {
  68. const latlng = libTransform.gcj02towgs84(pos.x, pos.y)
  69. return {
  70. x: latlng[0],
  71. y: latlng[1]
  72. }
  73. }
  74. window.AMapWith84__ = { //在Potree里setLonlat时不管转不转效果都一样 很奇怪,所以这里改名。只在ces
  75. aMapToWgs84, wgs84ToAMap
  76. }
  77. }
  78. //江门本地版本
  79. export const enter = ({ dom, mapDom, isLocal, lonlat, scenes, laserRoot, laserOSSRoot, panoOSSRoot,ossRoot }) => {
  80. console.warn('新的页面')
  81. Potree.settings.isOfficial = true //标记为正式、非测试版本
  82. //Potree.fileServer = axios
  83. Potree.settings.libsUrl = './lib/'
  84. let loadStartTime = Date.now()
  85. //正式环境(本地调试会打不开)
  86. if (location.host === 'mix3d.4dkankan.com') {
  87. Potree.settings.urls.prefix = Potree.settings.urls.prefix6
  88. Potree.settings.webSite = 'datav1'
  89. } else if (location.host === 'xfhd.4dkankan.com') {
  90. Potree.settings.urls.prefix = Potree.settings.urls.prefix7
  91. Potree.settings.webSite = 'datav1'
  92. }
  93. if(window.offline){//离线版
  94. Potree.settings.urls.templates = {
  95. depthTex : 'swss/{sceneCode}/www/{sceneCode}/wwwroot/{sceneCode}/data/{sceneCode}/depthmap',
  96. vision : '/swkk/{sceneCode}/wwwroot/scene_view_data/{sceneCode}/images/vision.txt'
  97. }
  98. Potree.fileStorage = {
  99. get(url){
  100. return new Promise(async function(resolve,reject){
  101. try {
  102. let data = await window.offlineData[url]
  103. if(data){
  104. resolve(data)
  105. }
  106. }catch(e){
  107. console.error('没找到', url, e)
  108. reject()
  109. }
  110. })
  111. }
  112. }
  113. }else{
  114. Potree.settings.cloudAddMapping = true
  115. }
  116. if(laserRoot != void 0){
  117. Potree.settings.urls.prefix = Potree.settings.urls.handlePrefix(laserRoot)
  118. }
  119. if(laserOSSRoot != void 0){
  120. Potree.settings.urls.setPrefix(1, laserOSSRoot) //dep ..
  121. }
  122. if(panoOSSRoot != void 0){
  123. Potree.settings.urls.setPrefix(3, panoOSSRoot) //tile
  124. Potree.settings.urls.setPrefix(8, panoOSSRoot)//vision.txt
  125. //Potree.settings.urls.panoPrefix = panoOSSRoot //vision.txt
  126. }
  127. if(ossRoot){
  128. Potree.settings.urls.ossRoot/* panoPrefix */ = Potree.settings.urls.handlePrefix(ossRoot) //vision.txt
  129. }
  130. Potree.settings.webSite = 'datav1' //depthTex
  131. const mapBus = mitt(), sceneBus = mitt()
  132. let isLocal2 = true
  133. if(isLocal2){//本地配置
  134. Potree.settings.isLocal = Potree.settings.tileOriginUrl = isLocal2
  135. }
  136. Potree.settings.showCompass = true
  137. Potree.settings.compassDom = dom.querySelector('#direction')
  138. Potree.settings.mergeType2 = true //标识新版
  139. Potree.settings.modelSkybox = true //是否将全景图贴在模型上(会导致卡顿)。若不显示模型将不显示Reticule
  140. Potree.settings.tiles3DMaxMemory = 300 //稍微增加点
  141. Potree.settings.mergeTransCtlOnClick = true
  142. Potree.settings.canWalkThroughModel = true
  143. window.cesErrorWords = '内存占用过高,建议关闭部分场景或升级显卡。'
  144. window.cesErrorCallback = ()=>{
  145. //sdk.setBackdrop('none')
  146. }
  147. let { THREE } = Potree.mergeEditStart(dom, mapDom, {
  148. queryCloudLonLatUrl:Potree.settings.urls.prefix+"/laser/4dage/{sceneCode}/getDataSetAndControlPoint"
  149. })
  150. let {MergeEditor, AnimationEditor} = viewer.modules
  151. Potree.settings.unableNavigate = true
  152. Potree.setLonlat(lonlat[0], lonlat[1])
  153. if(window.offline){//离线版 改目录
  154. viewer.images360.tileDownloader.getTiles = function(d, sceneNum, useV4url, model){
  155. let kankan = !model.isPointcloud //ModelTypes[model.props.fromType].panos4dkk
  156. if(kankan){
  157. return `/swkk/${sceneNum}/wwwroot/scene_view_data/${sceneNum}/images/${d}`
  158. }else{
  159. return `/swss/${sceneNum}/www/${sceneNum}/scene_view_data/${sceneNum}/images/${d}`
  160. }
  161. }
  162. }
  163. /* Potree.loadControlPoint = async function(callback,sceneCode,onError,prefix){//点云绑定地图
  164. let path = `/laser/jm/${sceneCode}/getDataSetAndControlPoint`
  165. return Potree.loadFile(path, {
  166. fetchMethod: 'post'
  167. }, callback,onError)
  168. } */
  169. //因为getPose里用的是target,俯视的yaw不准,所以限制一下不要完全俯视
  170. viewer.mainViewport.view.maxPitch-=0.001
  171. viewer.mainViewport.view.minPitch+=0.001
  172. viewer.addEventListener('camera_changed', e => {
  173. var camera = e.viewport.camera
  174. var pos = camera.position
  175. if (e.viewport.name == 'MainView' ) {
  176. sceneBus.emit('cameraChange', { x: pos.x, y: pos.y, z: pos.z, rotate: camera.rotation })
  177. Potree.Common.intervalTool.isWaiting('updateCamNear', ()=>{
  178. updateCamNear()
  179. }, 1000)
  180. updateCamFar()
  181. if(e.changeInfo.positionChanged){
  182. viewer.objs.children.forEach(model=>{
  183. model.result_.updateVisiByRange && model.result_.updateVisiByRange()
  184. })
  185. }
  186. }
  187. if (e.viewport.name == 'MainView' || e.viewport.name == 'top' ) {
  188. updateMap()
  189. }
  190. })
  191. viewer.addEventListener('shelterComputed', (e)=>{
  192. //console.log('shelterComputed')
  193. var camera = viewer.mainViewport.camera
  194. var pos = camera.position
  195. sceneBus.emit('cameraChange', { x: pos.x, y: pos.y, z: pos.z, rotate: camera.rotation })
  196. })
  197. //-------------------------------------
  198. let modelAinB = (A,B)=>{ //B的expand(5m) bound完全包含A
  199. let boundB = B.boundingBox.clone().expandByVector(new THREE.Vector3(5,5,5)).applyMatrix4(B.matrixWorld)
  200. let boundA = A.boundingBox.clone().applyMatrix4(A.matrixWorld)
  201. return boundB.containsBox(boundA)
  202. }
  203. let changeMeshVisi = (object, show) => {
  204. if(show == void 0) show = Potree.settings.displayMode == 'showPointCloud' || object == viewer.images360.currentPano.pointcloud && Potree.settings.modelSkybox || object.showInPano //showInPano: 装饰物,一直显示
  205. || !object.panos && modelAinB(object, viewer.images360.currentPano.pointcloud) //装饰物
  206. Potree.Utils.updateVisible(object, 'showPanos', show)
  207. }
  208. if(Potree.settings.canWalkThroughModel){
  209. let lastModel
  210. viewer.images360.addEventListener('flyToPano',(e)=>{//开始漫游 漫游到另一个模型就要选中这个模型?
  211. let model = e.toPano.pano.pointcloud
  212. if(lastModel != model){
  213. changeMeshVisi(model, true)
  214. //MergeEditor.selectModel(model)
  215. //model.result_.flyInPano(e.toPano.pano, {dontFly:true}) //切换模型显示,因为flyInPano有事件怕乱所以统一用这个函数
  216. updateCamNear()
  217. }
  218. })
  219. viewer.images360.addEventListener('flyToPanoDone',(e)=>{
  220. if(!e.makeIt)return
  221. let model = viewer.images360.currentPano.pointcloud
  222. if(lastModel != model){
  223. lastModel?.isModel && changeMeshVisi(lastModel, false)
  224. sceneBus.emit('panoModelChange', model.result_ )
  225. }
  226. lastModel = model
  227. })
  228. }
  229. viewer.images360.addEventListener('endChangeMode',(e)=>{
  230. sceneBus.emit('modeChange', {mode: e.mode == 'showPanos' ? 'pano' : 'fuse', model : e.mode == 'showPanos' && viewer.images360.currentPano.pointcloud.result_} )
  231. Potree.Utils.updateVisible(MergeEditor.transformControls, 'showPanos', e.mode == 'showPointCloud')
  232. Potree.Utils.updateVisible(MergeEditor.boxHelper, 'showPanos', e.mode == 'showPointCloud')
  233. if(e.mode == 'showPanos'){
  234. viewer.setControls( viewer.fpControls )
  235. viewer.removeEventListener('camera_changed', camera_changed)
  236. }else{
  237. viewer.addEventListener('camera_changed', camera_changed)
  238. }
  239. viewer.objs.children.forEach((e)=>{changeMeshVisi(e)})
  240. Potree.settings.canWalkThroughModel || viewer.images360.panos.forEach(pano => {
  241. pano.setEnable(e.mode == 'showPanos' ? pano.pointcloud == viewer.images360.currentPano.model : true)
  242. })
  243. Potree.settings.unableNavigate = e.mode == 'showPointCloud'
  244. updateCamNear()
  245. })
  246. let camera_changed = (e) => {
  247. if (e.viewport.name == 'MainView' && e.changeInfo.positionChanged) {
  248. //viewer.mainViewport.camera.position
  249. viewer.mainViewport.view.radius = 0.1 //使pivot在面前一丢丢距离
  250. viewer.setControls(viewer.orbitControls)
  251. viewer.removeEventListener('camera_changed', camera_changed)
  252. }
  253. }
  254. let requestInPano = false
  255. //-------------------------------------
  256. /* viewer.inputHandler.addEventListener('keydown', (e)=>{
  257. if(e.event.key == "e" ){
  258. MergeEditor.transformControls.mode = 'rotate'
  259. }else if(e.event.key == "w"){
  260. MergeEditor.transformControls.mode = 'translate'
  261. }else if(e.event.key == "s"){
  262. MergeEditor.transformControls.mode = 'scale'
  263. }
  264. }) */
  265. viewer.addEventListener('webglError', e => {
  266. console.error('viewer webglError: ' + e)
  267. let memory = '. \n jsHeapSizeLimit:'+ performance.memory.jsHeapSizeLimit/ 1e6 + ', usedJSHeapSize: '+performance.memory.usedJSHeapSize/ 1e6 + '(M)'
  268. sceneBus.emit('webglError', { msg: e.msg + memory })
  269. })
  270. viewer.compass.setAutoDisplay(true)
  271. /* mapBus.on('visible', v => {
  272. //console.log('mapBus visible', v)
  273. viewer.mapViewer.visible = v
  274. if (v) {
  275. viewer.mapViewer.mapLayer.needUpdate = true
  276. }
  277. viewer.mapViewer.dispatchEvent({type:'forceVisible',visible:v})
  278. }) */
  279. {
  280. let index = 1;
  281. //let setDisplay()
  282. if (!Potree.isIframeChild) {
  283. /* viewer.addEventListener('createIframe',(e)=>{//创建了子页面
  284. }) */
  285. window.winIndex = 0;
  286. window.iframeCreated = function (iframe) {
  287. let child = iframe.contentWindow
  288. child.winIndex = index++
  289. //案件里视图提取页面子页面覆盖了父级页面,父级的模型可以隐藏以释放内存
  290. console.error('createdIframe', child.winIndex, child.location.href)
  291. viewer.setDisplay(false)
  292. child.beforeDestroy = function () { //注:在前端仍会找不到beforeDestroy,可能contentWindow变更??所以手动调用setDisplay
  293. console.warn('beforeDestroy', child.winIndex)
  294. child.viewer && child.viewer.setDisplay(false)
  295. //如果是四维看看的场景,先不管了,页面被销毁应该就没了吧
  296. viewer.setDisplay(true)//恢复主页的模型显示
  297. if (!child.viewer) {
  298. try {
  299. let player = child.__sdk.core.get('Player')
  300. /* let runtime = player.model._3dTilesRuntime
  301. let tileset = runtime.getTileset()
  302. tileset._cache.trim(); //使下一次update时dispose所有不可见的tiles
  303. let sceneRenderer = child.__sdk.core.get('SceneRenderer')
  304. player.model.visible = false
  305. runtime.update(16, sceneRenderer.renderer, sceneRenderer.camera, true) //没用,为何_trimTiles的while无法进入
  306. */
  307. player.model.traverse(e => {
  308. e.geometry && e.geometry.dispose()
  309. if (e.material) {
  310. e.material.map && e.material.map.dispose()
  311. if (e.material.uniforms && e.material.uniforms.map && e.material.uniforms.map.value) {
  312. e.material.uniforms.map.value.dispose()
  313. }
  314. }
  315. }) //效果甚微
  316. /* let sceneRenderer = child.__sdk.core.get('SceneRenderer')
  317. sceneRenderer.renderer.render(sceneRenderer.scene, sceneRenderer.camera)
  318. */
  319. } catch (e) {
  320. console.log(e)
  321. }
  322. }
  323. }
  324. }
  325. //不知道删除iframe时是否那些模型还在内存里,需要释放吗? 如果要需要加一个事件
  326. } else {
  327. }
  328. }
  329. window.THREE = THREE
  330. //isLocal = false
  331. let autoLoads = /* window.autoLoads = */ []
  332. let readyToAddModel
  333. let mainBackground = viewer.background
  334. const units = { 1: 'metric', 2: 'imperial' }
  335. let getMeasureType = function (type, unit = 1) {
  336. let info
  337. switch (type) {
  338. case 'free':
  339. info = { measureType: 'Distance' }
  340. break
  341. case 'area':
  342. info = { measureType: 'Area' }
  343. break
  344. case 'vertical':
  345. info = { measureType: 'Ver Distance' }
  346. break
  347. default:
  348. console.error('无此 measure type')
  349. }
  350. info.unit = units[unit]
  351. return info
  352. }
  353. let getMeasureFunction = function (measure, bus) {
  354. measure.addEventListener('highlight', (e) => {
  355. if(measure.type == 'Path'){
  356. bus.emit(e.state ? 'enter' : 'leave')
  357. }else{
  358. bus.emit('highlight', e.state)
  359. }
  360. })
  361. let update = (e)=>{ //拖拽结束后发送changeCallBack
  362. if (measure.parent) {
  363. //未被删除
  364. console.warn('changePoints', measure.dataset_points.length )
  365. if(measure.type == 'Path'){
  366. bus.emit('changePoints', measure.dataset_points.map((p,i)=>{return {
  367. position: (p || measure.points[i]).clone(),
  368. modelId: measure.points_datasets[i] == void 0 ? Id_noIntersect : measure.points_datasets[i],
  369. name: measure.markerLabels[i].originText
  370. }}))
  371. }else{
  372. bus.emit('update', [
  373. measure.dataset_points.map(p => p.clone()),
  374. measure.points_datasets
  375. ])
  376. }
  377. }
  378. }
  379. measure.addEventListener('marker_dropped', update)
  380. measure.addEventListener('changed', update)
  381. measure.addEventListener('createDone', update)
  382. measure.addEventListener('changeByHistory', update);
  383. return {
  384. /* quit: () => {
  385. Potree.Log('quit结束且删除: ' + measure.id, '#00c7b2')
  386. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure })
  387. }, //触发结束。退出测量模式,清除之前操作 */
  388. destroy: () => {
  389. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure })
  390. viewer.scene.removeMeasurement(measure)
  391. },
  392. /* getPoints: () => {
  393. return measure.points
  394. },
  395. getDatasetLocations: () => {
  396. return measure.dataset_points
  397. },
  398. getDatasets: () => {
  399. return measure.points_datasets
  400. },
  401. getDatasetId: () => {
  402. return measure.datasetId
  403. }, */
  404. getArea: () => {
  405. return measure.area //{value:area, string:..}
  406. },
  407. getDistance: () => {
  408. if (measure.points.length < 2) return 0
  409. var value = measure.points[0].distanceTo(measure.points[1])
  410. return {
  411. value, //米
  412. string: measure.getConvertString(value, 'distance')
  413. }
  414. },
  415. //手动开启或关闭:
  416. show: () => {
  417. Potree.Utils.updateVisible(measure, 'inListByUser', true)
  418. },
  419. hide: () => {
  420. Potree.Utils.updateVisible(measure, 'inListByUser', false)
  421. },
  422. fly() {
  423. if(measure.type == 'Path') Potree.settings.displayMode = 'showPointCloud'
  424. let result = viewer.focusOnObject(measure, 'measure', 1200, {dontLookUp:measure.type == 'Path', maxDis: measure.fadeFar && measure.fadeFar*1.5})
  425. return result.msg ? result.msg : result.promise
  426. //返回值 1 deferred 表示即将位移 2 'posNoChange' 表示已在最佳位置 3 'tooFar' 表示距离最佳位置太远
  427. },
  428. changeSelect(isHight) {
  429. //console.log('2d->3d isHight ', isHight)
  430. measure.setSelected(isHight, 'byList')
  431. },
  432. }
  433. }
  434. let sdk = Potree.sdk = {
  435. sceneBus, mapBus,
  436. canTurnToPanoMode(pos, far=Potree.config.panoFieldRadius) {
  437. pos = pos ? new THREE.Vector3().copy(pos) : viewer.images360.position
  438. let pano = viewer.images360.findNearestPano(pos)
  439. if (pano && pano.position.distanceTo(pos) < far * pano.pointcloud.scale.x) {
  440. return {model:pano.pointcloud.result_}
  441. }
  442. //poschange后会调用这个,如果返回false会变为点云模式,且不会自动变回原先的模式
  443. },
  444. getPositionByScreen(pos2d, hopeModelId) {//通过屏幕坐标获取真实坐标 . hopeModelId: 如果指定了模型,优先返回hopeModelId上的intersect
  445. //console.log('getPositionByScreen',hopeModelId)
  446. hopeModelId = null
  447. let worldPos, localPos, modelId, intersect, normal, localNormal
  448. let Handler = viewer.inputHandler
  449. let reGet = () => {//不使用当前鼠标所在位置的intersect,单独算
  450. pos2d.clientX = pos2d.x
  451. pos2d.clientY = pos2d.y
  452. pos2d.onlyGetIntersect = true
  453. pos2d.whichPointcloud = true
  454. if (hopeModelId != void 0) {//隐藏其他的模型
  455. let models = MergeEditor.getAllObjects()
  456. models.forEach(model => {
  457. Potree.Utils.updateVisible(model, 'forPick', model.dataset_id == hopeModelId)
  458. })
  459. }
  460. let intersect2 = Handler.onMouseMove(pos2d)
  461. if (hopeModelId != void 0) {//恢复
  462. let models = MergeEditor.getAllObjects()
  463. models.forEach(model => {
  464. Potree.Utils.updateVisible(model, 'forPick', true)
  465. })
  466. }
  467. if (intersect2 && intersect2.location) {
  468. intersect = intersect2
  469. }
  470. }
  471. if (pos2d && pos2d.inDrag) {
  472. reGet()
  473. } else {
  474. intersect = Handler.intersect
  475. if (intersect) {
  476. modelId = intersect.pointcloud ? intersect.pointcloud.dataset_id : intersect.object.dataset_id
  477. if (hopeModelId != void 0 && modelId != hopeModelId) {
  478. reGet()
  479. }
  480. }
  481. }
  482. if (intersect && intersect.location) {
  483. modelId = intersect.pointcloud ? intersect.pointcloud.dataset_id : intersect.object.dataset_id
  484. /* if(hopeModelId != void 0 && modelId != hopeModelId){
  485. return null
  486. } */
  487. worldPos = intersect.location.clone()
  488. localPos = Potree.Utils.datasetPosTransform({ toDataset: true, datasetId: modelId, position: worldPos })
  489. normal = intersect.normal
  490. localNormal = intersect.localNormal
  491. } else return null
  492. return { worldPos, modelId, normal, localPos, localNormal }
  493. },
  494. getScreenByPosition(pos3d, modelId, canShelter/* , disToCameraLimit */) {//通过模型局部坐标获取屏幕坐标
  495. //console.log('getScreenByPoint ')
  496. let isLocal = modelId != void 0
  497. pos3d = new THREE.Vector3().copy(pos3d)
  498. let worldPos = isLocal ? Potree.Utils.datasetPosTransform({ fromDataset: true, datasetId: modelId, position: pos3d }) : pos3d
  499. if (!worldPos) return
  500. if (canShelter) {
  501. if (viewer.inputHandler.ifBlockedByIntersect(worldPos, 0.1, true)) return { trueSide: false };
  502. }
  503. var viewport = viewer.mainViewport
  504. var camera = viewport.camera
  505. var dom = viewer.renderArea
  506. /* if (tagLimitDis != void 0) {
  507. if (camera.position.distanceToSquared(worldPos) > Math.pow(tagLimitDis, 2)) return false
  508. } */
  509. //console.log('getScreenByPoint ' + pos3d.toArray())
  510. return Potree.Utils.getPos2d(worldPos, viewport, dom)
  511. },
  512. setCameraFov(fov) {
  513. viewer.setFOV(fov)
  514. },
  515. screenshot: (width, height/* , bgOpacity=0 */ ) => {//
  516. //截图
  517. let bgOpacity = Potree.settings.showCesium ? 0 : 1 /* viewer.background == 'skybox' */ //因为要画map底图所以上层只能透明。之后需要的话再改
  518. console.log('bgOpacity', bgOpacity)
  519. Potree.Utils.updateVisible(MergeEditor.boxHelper, 'screenshot', false)
  520. Potree.Utils.updateVisible(viewer.scene.overlayScene, 'screenshot', false) //hide all
  521. var { getImagePromise, finishPromise } = viewer.startScreenshot({ type: 'default', /* useRenderTarget:true, */bgOpacity }, width, height)
  522. var deferred = $.Deferred();
  523. finishPromise.done(({ dataUrl }) => {
  524. if(Potree.settings.displayMode != 'showPanos' && Potree.settings.showCesium){//need map background
  525. Potree.cesScreenshot(width, height).done((mapBGurl)=>{
  526. let img = new Image(); img.src = dataUrl
  527. let imgBG = new Image(); imgBG.src = mapBGurl
  528. let loadCount = 0
  529. img.onload = imgBG.onload = ()=>{
  530. loadCount++;
  531. if(loadCount == 2){
  532. let url = Potree.Common.imgAddLabel(imgBG,img,{leftRatioToImg:0,topRatioToImg:0})
  533. deferred.resolve(url)
  534. }
  535. }
  536. })
  537. }else{
  538. deferred.resolve(dataUrl)
  539. }
  540. Potree.Utils.updateVisible(MergeEditor.boxHelper, 'screenshot', true)
  541. Potree.Utils.updateVisible(viewer.scene.overlayScene, 'screenshot', true)
  542. })
  543. return deferred.promise()
  544. },
  545. getPose() {//获取当前点位和朝向
  546. const camera = viewer.scene.getActiveCamera()
  547. const target = viewer.scene.view.getPivot().clone()
  548. const position = viewer.scene.view.position.clone()
  549. const pose = { position, target, displayMode:Potree.settings.displayMode }
  550. if(Potree.settings.displayMode == 'showPanos'){
  551. let model = viewer.images360.currentPano.pointcloud
  552. pose.panoId = viewer.images360.currentPano.originID
  553. pose.model = model.result_
  554. pose.posInModel = Potree.Utils.datasetPosTransform({ toDataset: true, position: camera.position.clone(), object:model })
  555. pose.rotInModel = Potree.Utils.datasetRotTransform({ toDataset: true, quaternion: camera.quaternion.clone(), getQuaternion: true, pointcloud:model }).toArray() //拿第一个数据集
  556. }
  557. //console.log('getPose',position, target)
  558. return pose
  559. },
  560. comeTo(o = {}) {
  561. //console.log('comeTo',o.position, o.target)
  562. //飞到某个点
  563. let deferred = $.Deferred()
  564. if(o.panoId != void 0){
  565. let model = o.model.model
  566. let pano = model.panos.find(a=>a.originID == o.panoId)
  567. if(pano){
  568. o.rotInModel = new THREE.Quaternion().fromArray(o.rotInModel)
  569. let quaternion = Potree.Utils.datasetRotTransform({ fromDataset: true, quaternion: o.rotInModel, getQuaternion: true, object:model})
  570. o.model.flyInPano(pano, {quaternion, duration:0, callback(){
  571. o.callback && o.callback()
  572. deferred.resolve(true)
  573. }})
  574. return deferred.promise()
  575. }else{
  576. console.warn('没有找到漫游点',o)
  577. }
  578. }/* else if(requestInPano){
  579. requestInPano.result_.flyOutPano()
  580. } */else{
  581. if (o.modelId != void 0) {
  582. ['position', 'target'].forEach(e => {
  583. if (o[e]) {
  584. o[e] = Potree.Utils.datasetPosTransform({ fromDataset: true, datasetId: o.modelId, position: o[e] })
  585. }
  586. })
  587. }
  588. }
  589. if (o.distance || o.maxDis) {
  590. //o.isFlyToTag = true
  591. let requestShowPano
  592. let position = o.target || o.position
  593. if(o.isFlyToTag){
  594. let r = sdk.canTurnToPanoMode(position, 5)//热点新需求:如果附近有漫游点就飞到全景模式
  595. if(r){
  596. requestShowPano = true
  597. }else{
  598. Potree.settings.displayMode = 'showPointCloud'
  599. }
  600. }
  601. let rusult = viewer.focusOnObject({ position }, 'tag', null, { distance: o.distance || 1, maxDis : o.maxDis, requestShowPano /* , checkIntersect:true */ })
  602. rusult.promise.then(()=>{
  603. if(o.isFlyToTag){
  604. Potree.settings.displayMode = requestShowPano ? 'showPanos' : 'showPointCloud'
  605. }
  606. })
  607. return rusult.promise
  608. }
  609. viewer.scene.view.setView($.extend({}, o, {
  610. duration: o.dur,
  611. callback: () => {
  612. o.callback && o.callback()
  613. deferred.resolve(true)
  614. }
  615. }))
  616. return deferred.promise()
  617. },
  618. setBackdrop(sky, type, { scale, rotate }={}) {//天空盒背景
  619. //console.log('天空盒背景', sky,type)
  620. let setGroundAndText = (color) => {
  621. MergeEditor.secondCompass.dom.find(".dirText").css({ 'color': color })
  622. viewer.compass.dom.find(".dirText").css({ 'color': color })
  623. MergeEditor.ground.material.uniforms.uColor.value.set(color)
  624. //MergeEditor.ground.children[0].material.color.set(color)
  625. }
  626. viewer.dispatchEvent('content_changed')
  627. if(type == 'map'){
  628. MergeEditor.setGroundPlaneImg(null)
  629. viewer.setBackground(mainBackground)
  630. Potree.settings.showCesium = true
  631. buildMap()
  632. viewer.backgroundOpacity = 0
  633. return
  634. }else{
  635. Potree.settings.showCesium = false
  636. }
  637. if (type == 'bimg') {//地面图
  638. MergeEditor.setGroundPlaneImg(sky, scale, rotate)
  639. setGroundAndText('#e0e0e0')
  640. viewer.setBackground(mainBackground)
  641. } else {
  642. MergeEditor.setGroundPlaneImg(null)
  643. if (sky == 'none') {
  644. viewer.setBackground(mainBackground)
  645. setGroundAndText('#eee')
  646. } else if (sky[0] == '#') {
  647. viewer.setBackground(new THREE.Color(sky))
  648. let color = sky == '#fff' ? '#666' : sky == '#333' ? '#eee' : '#bbb' //反相
  649. setGroundAndText(color)
  650. } else if (type == 'image-map' || type == 'vector-map') {//影像|矢量 地图
  651. } else {//环境
  652. sky = ossRoot + sky
  653. viewer.setBackground('skybox', sky)
  654. setGroundAndText('#e0e0e0')
  655. }
  656. }
  657. },
  658. switchMapType(type1,type2){
  659. if(window.location.href.includes('localhost:7173')/* || Potree.browser.urlHasValue('testMap') */ ) return
  660. console.log('switchMapType',type1,type2)
  661. mapProps = getMapProp(type1,type2)
  662. if(Potree.settings.showCesium){
  663. buildMapFromProp()
  664. }
  665. },
  666. enableMap(mapArea, latlng) {
  667. if (!viewer.mapViewer) {
  668. //--------------------------------
  669. viewer.mapViewer = new Potree.MapViewer(mapArea)
  670. viewer.mapViewer.initProjection()
  671. //focus
  672. let boundSize = new THREE.Vector3(200, 150, 1).max(viewer.bound.boundSize)
  673. viewer.mapViewer.addEventListener('viewerResize', () => {
  674. viewer.mapViewer.moveTo(viewer.bound.center, boundSize, 0)
  675. }, { once: true })
  676. }
  677. },
  678. enterSceneGuide(pathArr) {//导览 (不需要修改参数)
  679. let editor = viewer.modules.CamAniEditor
  680. console.log('pathArr', pathArr)
  681. //console.log('enterSceneGuide',pathArr)
  682. pathArr.forEach(e=>{
  683. if(e.panoId != void 0){
  684. e.model = e.model.model
  685. }
  686. })
  687. let data = {
  688. //duration: pathArr.slice(0, pathArr.length - 1).reduce(function (total, currentValue) { return total + currentValue.time }, 0), //总时长(要去掉最后一个,因为已到终点,该点time无意义)
  689. points: pathArr,
  690. useDurSlice: true
  691. }
  692. let ani = editor.createMulAnimation(data)
  693. //注:最多只存在一条导览
  694. let bus = mitt()
  695. //播放完成
  696. ani.event_.addEventListener('playDone', () => {
  697. bus.emit('playComplete')
  698. viewer.images360.panos.forEach(e=>e.marker && Potree.Utils.updateVisible(e, 'playAni', true))
  699. })
  700. //切换点
  701. ani.event_.addEventListener('updateCurrentIndex', e => {
  702. bus.emit('changePoint', e.currentIndex + 1)
  703. })
  704. return {
  705. bus,
  706. play() {
  707. MergeEditor.selectModel(null)
  708. viewer.images360.panos.forEach(e=>e.marker && Potree.Utils.updateVisible(e, 'playAni', false))
  709. ani.play()
  710. },
  711. pause() {
  712. viewer.images360.panos.forEach(e=>e.marker && Potree.Utils.updateVisible(e, 'playAni', true))
  713. ani.stop()
  714. },
  715. clear() {
  716. ani.remove()
  717. },
  718. }
  719. },
  720. //[path1, paht2], { time, speed }
  721. calcPathInfo(paths, info) { //传入的time, speed仅有一个。返回完整的 time, speed
  722. //这一版的control似乎无法在某个位置上改变角度,位置和角度一般都是一起变的,所以先不增加单位更换功能。
  723. let pos1 = new THREE.Vector3().copy(paths[0].position)
  724. let pos2 = new THREE.Vector3().copy(paths[1].position)
  725. let dis = pos1.distanceTo(pos2)
  726. if (info.time != void 0) {
  727. info.speed = dis / info.time
  728. } else {
  729. info.time = dis / info.speed
  730. }
  731. return info
  732. },
  733. addModel(props) {
  734. let model
  735. let bus = props.bus = mitt()
  736. //console.log('--addModel',props)
  737. props.isFirstLoad = isLocal ? props.bottom == void 0 : (props.isDynamicAdded || props.mode == 'single') // 在编辑时用户添加的 或 展示单个模型 (props.mode='single'模型展示页, props.mode='many'融合页)
  738. if (props.opacity == void 0) props.opacity = 100
  739. //if (props.type == 'obj') props.type = 'glb'
  740. props.scale && (props.scale /= 100)
  741. let getBaseRotation = () => {
  742. if(ModelTypes[props.fromType]?.rot90 && props.type != 'obj'){
  743. return new THREE.Euler(Math.PI / 2, 0, 0)
  744. } else return new THREE.Euler(0, 0, 0)
  745. }
  746. let getDefaultRotation = () => {
  747. if(model.lonLatRot){
  748. return model.lonLatRot
  749. }else{
  750. return getBaseRotation()
  751. }
  752. }
  753. if (props.rotation) {
  754. if (props.rotation._x == void 0 && props.rotation.x != void 0) {
  755. props.rotation = new THREE.Euler().setFromVector3(props.rotation)
  756. }
  757. }
  758. props.baseRotation = getBaseRotation()
  759. props.is4dkkModel = ModelTypes[props.fromType].panos4dkk
  760. if (!props.isFirstLoad) {
  761. if (autoLoads.length == 0) { //首次加载
  762. setTimeout(() => {
  763. let sizes = autoLoads.map(e => e.size || 0)
  764. console.log('需要请求加载的模型大小为', sizes, '总大小', sizes.reduce(function (total, currentValue) {
  765. let current = parseFloat(currentValue)
  766. return total + ((typeof currentValue == 'number' || currentValue.includes('M')) ? current : current / 1024)
  767. }, 0))
  768. readyToAddModel = true //准备开始加载
  769. loadNext()//startLoad(autoLoads[0])
  770. }, 30)
  771. }
  772. autoLoads.push(props)
  773. readyToAddModel = false
  774. } else {
  775. readyToAddModel = true
  776. }
  777. let done = (model_) => {
  778. model = model_
  779. model.result_ = result
  780. model.props = props
  781. result.model = model
  782. model.fromType = ModelTypes[props.fromType].name
  783. if (!props.isFirstLoad) {
  784. model.visible = false//先不显示,防止卡顿
  785. }
  786. model.showInPano = /* !model.is4dkkModel// */props.raw.showInPano //现在不用这个,所有模型都可见,非is4dkkModel的还显示原本的贴图
  787. props.opacity < 100 && result.changeOpacity(props.opacity)
  788. model.addEventListener('changeSelect', (e) => {
  789. bus.emit('changeSelect', e.selected)
  790. })
  791. let lastState = {}
  792. model.addEventListener('transformChanged', (e) => {
  793. let msg = {byControl:!!e.byControl} //byControl代表是手动用控制轴修改 动画文件要改帧
  794. if (!lastState.position || !model.position.equals(lastState.position)) {
  795. lastState.position = msg.position = model.position.clone()
  796. //console.log('change pos', model.name, model.position.toArray())
  797. }
  798. if (!lastState.rotation || !model.rotation.equals(lastState.rotation)) {
  799. lastState.rotation = msg.rotation = model.rotation.clone()
  800. }
  801. if (lastState.scale == void 0 || model.scale.x * 100 != lastState.scale) {
  802. lastState.scale = msg.scale = model.scale.x * 100
  803. }
  804. msg = Potree.Common.CloneObject(msg)
  805. //console.log(model.name, msg)
  806. bus.emit('transformChanged', msg)
  807. })
  808. spliceFromArr(model, props, true)
  809. model.addEventListener('changeSelect', (e) => {
  810. MergeEditor.transformControls.visible && e.selected && MergeEditor.transformControls.attach(model, e.clickPos) //: MergeEditor.transformControls.detach()
  811. })
  812. MergeEditor.modelAdded(model)
  813. if (props.mode == 'single') {//模型查看页
  814. MergeEditor.noNeedSelection = true
  815. setTimeout(() => {
  816. MergeEditor.focusOn([model], 1000, true, true)
  817. }, 1)
  818. }
  819. if(ModelTypes[props.fromType].panos4dkk){
  820. Potree.load4dkkPanos(props.raw.num, model, getBaseRotation(), () => {
  821. bus.emit('loadDone',model)
  822. }, props.fromType == 0 ? '2k' : '4k' ) //看看场景是2k
  823. } else {
  824. bus.emit('loadDone',model)
  825. }
  826. //console.log('loadDone' )
  827. }
  828. let progressFun = (progress) => {
  829. bus.emit('loadProgress', progress)
  830. }
  831. let onError = function (xhr) {
  832. bus.emit('loadError', xhr)
  833. console.log('loadError!!!!!!!!!', Potree.Common.getNameFromURL(props.url), props.size, xhr)
  834. spliceFromArr(model, props, false)
  835. }
  836. //try {
  837. // props.url = JSON.parse(props.url) //去掉 '\'
  838. /* if(props.modelType != 'pointcloud' ){//当前本地版这么写
  839. let addPrefix = (e)=> Potree.settings.urls.getPrefix(3,props) + '/profile'+ e
  840. if(props.url instanceof Array){
  841. props.url = props.url.map(e=>addPrefix(e))
  842. }else{
  843. props.url = addPrefix(props.url)
  844. }
  845. } */
  846. //} catch (e) { }
  847. props.raw.panoRoot = props.raw.panoOSSRoot + (props.raw.mapping?('/'+props.raw.mapping):'')
  848. props.done = done; props.progressFun = progressFun; props.onError = onError
  849. if (readyToAddModel) {
  850. if (autoLoads.filter(e => e.loading).length < maxLoadingCount) {
  851. startLoad(props)
  852. }
  853. }
  854. let scaleMeasure
  855. let result = {
  856. bus,
  857. model,
  858. getDefaultRotation,
  859. supportPano() { //是否支持全景图
  860. return model?.panos?.length > 0
  861. },
  862. flyInPano(pano, {dontFly, quaternion, duration}={}) {// 飞入全景图
  863. requestInPano = model
  864. pano = pano || viewer.images360.findNearestPano(null, model.panos)
  865. if (pano) {
  866. dontFly || viewer.images360.flyToPano({ pano, canCancelLast: true, quaternion, duration})
  867. Potree.settings.displayMode = 'showPanos'
  868. }
  869. },
  870. flyOutPano() {// 飞出全景图(就是切换到正常融合视角)
  871. requestInPano = false
  872. Potree.settings.displayMode = 'showPointCloud'
  873. /* setTimeout(() => {//在下一帧再变,因为3dtiles需要更新一下才会显示tiles
  874. if (!requestInPano) {
  875. Potree.settings.displayMode = 'showPointCloud'
  876. Potree.Utils.updateVisible(MergeEditor.boxHelper, 'showPanos', true)
  877. }
  878. }, 50) */
  879. },
  880. changeShow(show) {
  881. props.show = show //for autoLoads show model
  882. if (model) {
  883. Potree.Utils.updateVisible(model, 'datasetSelection', show)
  884. if (model.panos) {
  885. model.panos.forEach(e => e.setEnable(show))
  886. }
  887. viewer.dispatchEvent('content_changed')
  888. }
  889. },
  890. changeSelect(state) {
  891. //console.error('select', state)
  892. if (model) {
  893. let fly = viewer.images360.latestRequestMode != 'showPanos'
  894. MergeEditor.selectModel(model, state, fly, true)
  895. updateCamNear()
  896. //console.log('changeSelect', props.id, state)
  897. }
  898. },
  899. changeScale(s) {
  900. if (model) {
  901. s /= 100
  902. if (model.scale.x == s) return
  903. //MergeEditor.history.beforeChange(model)//但不知道什么时候结束拖拽
  904. model.scale.set(s, s, s)
  905. model.isPointcloud && model.changePointSize(/* Potree.config.material.realPointSize * s */)
  906. model.dispatchEvent("scale_changed")
  907. }
  908. },
  909. changeOpacity(opacity) { //见笔记:透明物体的材质设置
  910. if (opacity == void 0) opacity = 100
  911. opacity /= 100
  912. MergeEditor.changeOpacity(model, opacity)
  913. },
  914. changeBottom(z) {
  915. /* model && MergeEditor.setModelBtmHeight(model,z)
  916. model.dispatchEvent('transformChanged') //改了position */
  917. },
  918. changePosition(pos) {//校准取消时执行
  919. //console.log('changePosition', model.name, pos.x, pos.y, pos.z)
  920. if(pos.x == 0 && pos.y == 0 && pos.z == 0 && model.lonLatPos ){
  921. model && model.position.copy(model.lonLatPos)
  922. console.log('changePosition 使用经纬度坐标', model.name )
  923. }else{
  924. model && model.position.copy(pos)
  925. }
  926. model.dispatchEvent({ type: 'position_changed' })
  927. },
  928. changeRotation(rot) {//校准取消时执行
  929. //console.log('changeRotation', model.name, rot.x, rot.y, rot.z)
  930. /* if(rot.x == 0 && rot.y == 0 && rot.z == 0 && model.lonLatRot ){
  931. model && model.rotation.copy(model.lonLatRot)
  932. console.log('changePosition 使用经纬度坐标', model.name )
  933. }else{ */
  934. model && model.rotation.setFromVector3(rot)
  935. //}
  936. model.dispatchEvent({ type: 'rotation_changed' , by2d:true})
  937. },
  938. enterRotateMode() {
  939. if (model) {
  940. if (MergeEditor.split) {//分屏校准
  941. MergeEditor.setTransformState('rotate')
  942. MergeEditor.transformControls2.attach(model)
  943. MergeEditor.transformControls2.mode = 'rotate'
  944. }
  945. MergeEditor.transformControls.attach(model)
  946. MergeEditor.transformControls.mode = 'rotate'
  947. }
  948. },
  949. enterMoveMode() {
  950. console.log('enterMoveMode')
  951. if (model) {
  952. if (MergeEditor.split) {//分屏校准
  953. MergeEditor.setTransformState('translate')
  954. MergeEditor.transformControls2.attach(model)
  955. MergeEditor.transformControls2.mode = 'translate'
  956. }
  957. MergeEditor.transformControls.attach(model)
  958. MergeEditor.transformControls.mode = 'translate'
  959. }
  960. },
  961. leaveTransform() {
  962. console.log('leaveTransform')
  963. if (MergeEditor.split) {//分屏校准
  964. MergeEditor.setTransformState(null)
  965. } else {
  966. MergeEditor.transformControls.detach()
  967. MergeEditor.transformControls2.detach()
  968. }
  969. MergeEditor.history.clear()
  970. },
  971. enterAlignment() {//开始校准
  972. result.leaveTransform()
  973. MergeEditor.enterSplit()
  974. if(Potree.settings.showCesium){
  975. cesiumViewer.scene.canvas.style.width = '50%'
  976. //cesiumViewer.resize()
  977. }
  978. let bus = new mitt()
  979. return {
  980. bus
  981. }
  982. },
  983. leaveAlignment() {
  984. //console.log('leaveAlignment',model.position, model.rotation)
  985. MergeEditor.leaveSplit()
  986. MergeEditor.transformControls.detach()
  987. MergeEditor.transformControls2.detach()
  988. if(Potree.settings.showCesium){
  989. cesiumViewer.scene.canvas.style.width = ''
  990. updateMap()
  991. }
  992. },
  993. enterScaleSet() {//设置比例
  994. let bus = new mitt()
  995. let length, measureBuilded;
  996. //viewer.outlinePass.selectedObjects = []
  997. if (!Potree.Utils.isInsideFrustum(model.boundingBox.clone().applyMatrix4(model.matrixWorld), viewer.scene.getActiveCamera())) {
  998. MergeEditor.focusOn(model, 600)
  999. }
  1000. MergeEditor.getAllObjects().forEach(m => {//隐藏其他的模型
  1001. if (m != model) Potree.Utils.updateVisible(m, 'enterScaleSet', false)
  1002. })
  1003. result.oldFar = Potree.settings.cameraFar
  1004. model.enterScaleOldState_ = {
  1005. //view: viewer.mainViewport.view.clone(),
  1006. scale: model.scale.x,
  1007. far: Potree.settings.cameraFar
  1008. }
  1009. let setScale = () => {
  1010. if (length == void 0 || !measureBuilded) return
  1011. let vec = new THREE.Vector3().subVectors(viewer.mainViewport.camera.position, scaleMeasure.points[1])
  1012. let dis = scaleMeasure.points[0].distanceTo(scaleMeasure.points[1])
  1013. let s = length / Math.max(dis,0.00001)
  1014. result.changeScale(model.scale.x * s * 100)
  1015. /* setTimeout(()=>{
  1016. viewer.focusOnObject(scaleMeasure , 'measure', 500)
  1017. },1) */
  1018. let newCamPos = new THREE.Vector3().addVectors(scaleMeasure.points[1], vec.multiplyScalar(s))
  1019. viewer.scene.view.setView({
  1020. position: newCamPos, target: scaleMeasure.getCenter(), duration: 0, callback: () => {
  1021. //更改target到measure中心的好处就是可以让相机绕measure中心转,坏处是每次更改都会变一下画面
  1022. //Potree.settings.cameraFar = Math.max(model.enterScaleOldState_.far, viewer.scene.view.position.distanceTo(model.boundCenter) + model.boundingBox.clone().applyMatrix4(model.matrixWorld).getSize(new THREE.Vector3).length())
  1023. //use updateCamFar()
  1024. }
  1025. })
  1026. }
  1027. return {
  1028. bus,
  1029. setLength(v) {
  1030. if (!v) return
  1031. length = v
  1032. setScale()
  1033. },
  1034. startMeasure() {
  1035. if (scaleMeasure) {
  1036. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure: scaleMeasure })
  1037. viewer.scene.removeMeasurement(scaleMeasure)
  1038. }
  1039. measureBuilded = false
  1040. scaleMeasure = viewer.measuringTool.startInsertion(
  1041. { measureType: "Distance", unit: "metric" },
  1042. () => {
  1043. //done:
  1044. //bus.emit('end' ) //完成
  1045. measureBuilded = true
  1046. setScale()
  1047. },
  1048. () => {
  1049. //cancel
  1050. //bus.emit('quit') //删除
  1051. }
  1052. )
  1053. scaleMeasure.forbitRepeatPoint = true //两个点不能相同,否则长度是0
  1054. scaleMeasure.addEventListener('marker_dropped', (e) => {//拖拽结束后发送changeCallBack
  1055. if (scaleMeasure.parent) {
  1056. //未被删除
  1057. measureBuilded && setScale()
  1058. }
  1059. })
  1060. }
  1061. }
  1062. },
  1063. leaveScaleSet() {
  1064. if (scaleMeasure) {
  1065. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure: scaleMeasure })
  1066. viewer.scene.removeMeasurement(scaleMeasure)
  1067. scaleMeasure = null
  1068. }
  1069. //viewer.outlinePass.selectedObjects = [model];
  1070. MergeEditor.getAllObjects().forEach(m => {//恢复其他的模型
  1071. if (m != model) Potree.Utils.updateVisible(m, 'enterScaleSet', true)
  1072. })
  1073. setTimeout(()=>{//可能还原了 相机位置移动回去
  1074. if(model.scale.x == model.enterScaleOldState_.scale){
  1075. MergeEditor.focusOn(model, 0) //reset orbitcontrol's minRadius //viewer.mainViewport.view.copy(model.enterScaleOldState_.view)
  1076. //Potree.settings.cameraFar = model.enterScaleOldState_.far
  1077. }
  1078. },10)
  1079. },
  1080. destroy() {
  1081. model && MergeEditor.removeModel(model)
  1082. result.changeSelect(false)
  1083. viewer.dispatchEvent('content_changed')
  1084. }
  1085. }
  1086. return result
  1087. },
  1088. //测量线的点都附着于各个模型,当模型变化时,点跟着变化。
  1089. // 新的测量创建方法,传入type 返回新测量对象
  1090. startMeasure(type) {
  1091. // 寻创建的测量对象有上面绘画测量对象的所有方法
  1092. const bus = mitt()
  1093. let info = getMeasureType(type)
  1094. let measure = viewer.measuringTool.startInsertion(
  1095. info,
  1096. () => {
  1097. //done:
  1098. bus.emit('submit')
  1099. },
  1100. () => {
  1101. //cancel
  1102. bus.emit('cancel'/* , ret */) //删除
  1103. }
  1104. )
  1105. Potree.Log('startMeasure: ' + measure.id, '#00c7b2')
  1106. /* let cancel = ()=>{
  1107. Potree.Log('clear删除: ' + measure.id, '#00c7b2')
  1108. viewer.dispatchEvent({ type: 'cancel_insertions', remove: true, measure })
  1109. viewer.scene.removeMeasurement(measure)
  1110. } */
  1111. let result = {
  1112. bus,
  1113. ...getMeasureFunction(measure, bus),
  1114. }
  1115. /* StartMeasure = Measure & {
  1116. // 多了cancel 取消测量的事件,没有参数
  1117. // 多了invalidPoint 当用户测量了无效点时的事件,抛出无效原因
  1118. bus: Emitter<{ cancel: void; invalidPoint: string }>
  1119. } */
  1120. return result
  1121. },
  1122. // 绘画测量线(非新增使用)
  1123. // type = 'free' (自由) || 'vertical' (垂直) || 'area' (面积)
  1124. // positions 点数组 构成如下 [{ point: {x,y,z}, modelId: 1 }]
  1125. drawMeasure(type, dataset_points, points_datasets) {
  1126. // 返回测量对象有如下
  1127. const bus = mitt()
  1128. let info = getMeasureType(type /* , unit */)
  1129. //info.points = positions
  1130. info.dataset_points = dataset_points
  1131. info.points_datasets = points_datasets
  1132. //info.sid = sid
  1133. info.bus = bus
  1134. let measure = viewer.measuringTool.createMeasureFromData(info)
  1135. if (!measure) return { bus }
  1136. Potree.Log('drawMeasure由数据新建: ' + measure.id, '#00c7b2')
  1137. let result = {
  1138. bus,
  1139. setPositions(dataset_points, points_datasets) {//用于恢复measure的点,不会修改点的个数
  1140. measure.dataset_points = dataset_points.map(e => {
  1141. return e && new THREE.Vector3().copy(e)
  1142. })
  1143. measure.points_datasets = points_datasets
  1144. measure.points = measure.dataset_points.map((p, i) => {
  1145. return Potree.Utils.datasetPosTransform({ fromDataset: true, datasetId: measure.points_datasets[i], position: p })
  1146. })
  1147. measure.getPoint2dInfo(measure.points)
  1148. measure.update({ ifUpdateMarkers: true })
  1149. measure.setSelected(false)//隐藏edgelabel
  1150. },
  1151. ...getMeasureFunction(measure, bus),
  1152. }
  1153. return result
  1154. },
  1155. /* export type PathProps = {
  1156. // 线段名称
  1157. name: string,
  1158. // 是否显示名称,
  1159. showName: boolean,
  1160. // 文字大小
  1161. fontSize: number,
  1162. // 是否显示方向,
  1163. showDirection: boolean,
  1164. // 方向是否反向
  1165. reverseDirection: boolean,
  1166. line: {
  1167. width: number,
  1168. color: string,
  1169. altitudeAboveGround: number
  1170. position: SceneLocalPos,
  1171. normal: SceneLocalPos,
  1172. modelId: string
  1173. },
  1174. points: {
  1175. // 点位名称
  1176. name: string,
  1177. position: SceneLocalPos,
  1178. modelId: string,
  1179. }[]
  1180. }
  1181. bus: Emitter<{
  1182. // 标注点击事件
  1183. click: void;
  1184. // 鼠标移入标注事件
  1185. enter: void;
  1186. // 鼠标移出标注事件
  1187. leave: void;
  1188. // 线段坐标更改事件
  1189. linePositionChange: {
  1190. pos: SceneLocalPos,
  1191. normal: SceneLocalPos,
  1192. modelId: string
  1193. }
  1194. // 路径点位置变更
  1195. changePoints: PathProps['points']
  1196. // 距离相机位置变更
  1197. toCameraDistanceChange: number
  1198. }>;
  1199. */
  1200. createPath(props){//路线
  1201. //console.log('createPath', props)
  1202. let bus = mitt()
  1203. let path
  1204. let info = {type : 'Path', minMarkers : 2, title:props.name, lineHeight : props.line.altitudeAboveGround }
  1205. if(props.points.length == 0){
  1206. path = viewer.measuringTool.startInsertion( info, () => {
  1207. bus.emit("drawed" ); //完成
  1208. })
  1209. viewer.dispatchEvent({ type: 'cancel_insertions', dontRemove: true, measure:path }) //要等进入编辑才能继续编辑
  1210. }else{
  1211. let originPointCount = props.points.length
  1212. props.points = props.points.filter(e=> isValidPoint(e.modelId))
  1213. info.points_datasets = props.points.map(e=> e.modelId == Id_noIntersect ? null : e.modelId)
  1214. info.dataset_points = info.points = props.points.map(e=>e.position)//当该点不在任何模型上时,记录的是世界坐标,所以两个都赋值,过后根据有无datasetID选择
  1215. path = viewer.measuringTool.createMeasureFromData(info);
  1216. if(props.line.position) {
  1217. if(isValidPoint(props.line.modelId)){
  1218. let pos = props.line.modelId == Id_noIntersect ? new THREE.Vector3().copy(props.line.position) :
  1219. Potree.Utils.datasetPosTransform({fromDataset:true, position: props.line.position, datasetId: props.line.modelId })
  1220. path.updateTitlePos(pos)
  1221. }else{
  1222. console.log('path label pos 因模型被删而去除', info.title )
  1223. }
  1224. }
  1225. if(props.points.length < originPointCount ) {
  1226. path.dispatchEvent('createDone')
  1227. console.log('path点因模型被删减少', info.title, originPointCount,'->',props.points.length)
  1228. }
  1229. }
  1230. {
  1231. let curSelectMarker
  1232. path.addEventListener('markerSelect',(e)=>{
  1233. let msg
  1234. if(e.cancel){
  1235. curSelectMarker == e.marker && (msg = -1) //是当前选中的marker就取消
  1236. }else{
  1237. curSelectMarker = e.marker
  1238. msg = path.markers.indexOf(e.marker)
  1239. }
  1240. //msg != void 0 && console.log('msg',msg)
  1241. msg != void 0 && bus.emit('activePoint', msg )
  1242. })
  1243. path.addEventListener('titlePosChanged',(e)=>{
  1244. //console.log('titlePosChanged',path.title, e.position.clone())
  1245. bus.emit('linePositionChange', {
  1246. modelId: e.root ? e.root.dataset_id : Id_noIntersect,
  1247. pos: e.root ? Potree.Utils.datasetPosTransform({toDataset:true, position: e.position.clone(), datasetId: e.root.dataset_id }) : e.position.clone()
  1248. })
  1249. })
  1250. path.addEventListener('chose',(e)=>{
  1251. switchSelect(e.state)
  1252. bus.emit('focus', e.state)
  1253. })
  1254. }
  1255. let funs = getMeasureFunction(path, bus)
  1256. let switchSelect = (state)=>{//切换选择,最多一个选中
  1257. if(state){
  1258. curSelectPath && curSelectPath.setSelected('unclick' ) //取消上一个选中的
  1259. curSelectPath = path
  1260. }else{
  1261. curSelectPath == path && (curSelectPath = null)
  1262. }
  1263. }
  1264. let functions = Object.assign(funs,{
  1265. bus,
  1266. path,
  1267. changeEditMode(state){//进入编辑
  1268. if(!state){
  1269. viewer.dispatchEvent({ type: 'cancel_insertions', dontRemove: true, measure:path })
  1270. }
  1271. path.setEditEnable(state)
  1272. },
  1273. changeCanEdit(state){//是否点击pen图标以加点和删点
  1274. if(state){
  1275. if(path.points.length < 2){//继续绘制
  1276. info.resume = true, info.measure = path
  1277. path = viewer.measuringTool.startInsertion( info, () => {
  1278. bus.emit("drawed" ); //完成
  1279. })
  1280. }
  1281. }else{
  1282. viewer.dispatchEvent({ type: 'cancel_insertions', dontRemove: true, measure:path })
  1283. }
  1284. console.log('changeCanEdit',state)
  1285. path.setAddOrRemPoint(state)
  1286. },
  1287. visibility(v){
  1288. //console.log('visibility', path.title, v)
  1289. Potree.Utils.updateVisible(path,'user', v)
  1290. },
  1291. visibilityName(v){
  1292. path.setTitleVisi(path.titleLabel.parent, v, 'user')
  1293. },
  1294. changeName(name){
  1295. path.setTitle(name)
  1296. },
  1297. changePointName(index,name){
  1298. path.setMarkerTitle(index, name)
  1299. },
  1300. changePathPoints(points){
  1301. console.log('changePathPoints??????????',points)
  1302. },
  1303. deletePoint(index){
  1304. path.removePoint(index)
  1305. },
  1306. changeFontSize(fontsize){
  1307. path.setFontSize(fontsize)
  1308. },
  1309. changeLine({width,color,altitudeAboveGround}){
  1310. path.setPathWidth(width)
  1311. path.setColor(color)
  1312. path.setLineHeight(altitudeAboveGround)
  1313. },
  1314. changeVisibilityRange(far){//设置消失距离
  1315. path.setFadeFar(far== -1 ? 0 : far)
  1316. },
  1317. highlight(state){
  1318. path.setSelected(state?'hover':'unhover', true)
  1319. },
  1320. focus(state){
  1321. switchSelect(state)
  1322. path.setSelected(state?'click':'unclick', true)
  1323. },
  1324. changeDirection(show,reverse){
  1325. path.setArrowDisplay(show)
  1326. path.setReverse(reverse)
  1327. },
  1328. createAni(tension){
  1329. let distance = path.totalLength
  1330. let pathPoints = path.points.map(e=>e.clone().add(new THREE.Vector3(0,0,1.5))) //在地面之上一定高度
  1331. if(path.reverse) pathPoints.reverse()
  1332. const speed = 3, //m/s
  1333. turnDisPerRad = 1.5,
  1334. maxTurnDis = 3,//拐弯最大距离
  1335. maxRoadTurnRatio = 0.8 //每段路单次转弯最大比例,防止拐弯占据一整条路直到下一个点
  1336. let roadLens = []
  1337. let vecs = pathPoints.map((p,i)=>{
  1338. if(i==0)return
  1339. let last = pathPoints[i-1]
  1340. roadLens.push(p.distanceTo(last))
  1341. return new THREE.Vector3().subVectors(p,last).normalize()
  1342. })
  1343. let turnDis = vecs.map((vec,i)=>{//在每个转折点拐弯前后需要的米数
  1344. if(i==0 || i==vecs.length-1)return 0
  1345. let next = vecs[i+1]
  1346. let angle = next.angleTo(vec)
  1347. return Math.min(turnDisPerRad * angle / 2, maxTurnDis )
  1348. })
  1349. let points = []
  1350. let len = pathPoints.length
  1351. for(let i=0;i<len;i++){
  1352. let thisPoint = pathPoints[i]
  1353. let nextPoint = pathPoints[i+1]
  1354. if(i==0 || i==len-1){//首尾的点直接加入,其他点不加
  1355. points.push({
  1356. position: thisPoint.clone(),
  1357. target: i==0 ? nextPoint.clone() : pathPoints[len-1].clone().add(vecs[len-1])
  1358. })
  1359. }
  1360. if(i<len-1){ //加入每段边要加入的两个(or一个)拐点 ,拐点之间方向沿着路径
  1361. let turnDis1 = Math.min(turnDis[i], roadLens[i] * maxRoadTurnRatio)
  1362. let turnDis2 = Math.min(turnDis[i+1], roadLens[i] * maxRoadTurnRatio)
  1363. let turnDisSum = turnDis1 + turnDis2 //两端拐弯距离之和。
  1364. if(turnDisSum > roadLens[i]){//如果超过了路长度, 该条路就只有一个拐点
  1365. turnDis1 = turnDis1 / turnDisSum * roadLens[i]
  1366. let p = thisPoint.clone().add(vecs[i+1].clone().multiplyScalar(turnDis1))
  1367. points.push({position: p, target: nextPoint.clone()})
  1368. }else{
  1369. if(turnDis1>0){ //i==0时为0
  1370. let turnPoint1 = thisPoint.clone().add(vecs[i+1].clone().multiplyScalar(turnDis1))
  1371. points.push({position: turnPoint1, target: nextPoint.clone()})
  1372. }
  1373. if(turnDis2>0){//i==len-2时为0
  1374. let turnPoint2 = nextPoint.clone().sub(vecs[i+1].clone().multiplyScalar(turnDis2))
  1375. points.push({position:turnPoint2, target: nextPoint.clone()})
  1376. }
  1377. }
  1378. }
  1379. }
  1380. //加后缀&test以看路线
  1381. let data = {
  1382. name : 'path_guideTour',
  1383. duration : distance / speed,
  1384. points,
  1385. tension
  1386. }
  1387. path.animation_ = viewer.modules.CamAniEditor.createAnimation(data)
  1388. },
  1389. play(playDone){
  1390. if(path.points.length < 2)return playDone && playDone() //no points
  1391. let oldStates = {
  1392. editEnable: path.editEnable,
  1393. addOrRemovePoint: path.addOrRemovePoint
  1394. }
  1395. path.editEnable && functions.changeEditMode(false)
  1396. path.addOrRemovePoint && path.setAddOrRemPoint(false)
  1397. if(Potree.settings.pathSmooth){
  1398. let curve = path.curve.clone();
  1399. curve.points.forEach(e=>e.z += 2)
  1400. if(path.reverse) curve.points.reverse()
  1401. //let geoPoints = path.geoPoints.map(e=> e.clone().add(new THREE.Vector3(0,0,2)) )//height
  1402. let duration = path.totalLength / 3
  1403. //let tangentDt = path.totalLength * 0.0001
  1404. path.animation_ = viewer.modules.CamAniEditor.createCurveAni(curve, duration )
  1405. }else{
  1406. functions.createAni();//不传参数时路径最圆润缓和,但会脱离原路径。传参后除了拐弯都按路径,参数越大越圆润,但容易有折回的bug。 如果没有严格要求就不传参效果最佳。
  1407. }
  1408. path.animation_.play()
  1409. path.animation_.addEventListener('playDone', () => {
  1410. oldStates.editEnable && functions.changeEditMode(true)
  1411. oldStates.addOrRemovePoint && path.setAddOrRemPoint(true)
  1412. playDone && playDone()
  1413. },{once:true})
  1414. },
  1415. pause(){
  1416. path.animation_?.pause()
  1417. path.animation_ && viewer.modules.CamAniEditor.removeAnimation(path.animation_)
  1418. path.animation_ = null
  1419. }
  1420. })
  1421. /* for(let i in functions){
  1422. if(functions[i] instanceof Function){
  1423. let oldFun = functions[i]
  1424. functions[i] = function(){
  1425. console.warn('path', i, path.title, ...arguments)
  1426. oldFun.apply(this, arguments)
  1427. }
  1428. }
  1429. } */
  1430. path.functions = functions
  1431. props.line && functions.changeLine(props.line)
  1432. return functions
  1433. },
  1434. startAddSth(){//开始添加热点
  1435. Potree.settings.disableClick = true //禁止点击事件,尤其是全景模式下,否则会走到下一个点
  1436. viewer.dispatchEvent('start_inserting_tag')
  1437. },
  1438. endAddSth(){
  1439. Potree.settings.disableClick = false
  1440. viewer.dispatchEvent('endTagMove')
  1441. },
  1442. createTagging(props){
  1443. let bus = mitt()
  1444. //console.warn('createTagging', props)
  1445. let root = viewer.scene.pointclouds.concat(viewer.objs.children).find(e=>e.dataset_id == props.modelId)
  1446. if(!root){
  1447. return console.error('热点没有找到该modelId,模型是否已经删除?')
  1448. }
  1449. let info = {
  1450. position: new THREE.Vector3().copy(props.position), //局部坐标
  1451. normal: props.normal ? new THREE.Vector3().copy(props.normal) : new THREE.Vector3(0,0,1),
  1452. root, lineLength: props.altitudeAboveGround,
  1453. title: props.title, fontsize: props.fontSize
  1454. }
  1455. let tag = viewer.tagTool.createTagFromData(info)
  1456. tag.addEventListener('mouseover',()=>{
  1457. bus.emit('enter')
  1458. })
  1459. tag.addEventListener('mouseleave',()=>{
  1460. bus.emit('leave')
  1461. })
  1462. tag.addEventListener('click',()=>{
  1463. bus.emit('click')
  1464. })
  1465. tag.addEventListener('posChanged',(e)=>{
  1466. bus.emit('changePosition', {
  1467. modelId: tag.root.dataset_id,
  1468. normal: tag.normal.clone(),
  1469. pos: tag.position.clone()
  1470. })
  1471. })
  1472. tag.functions = {
  1473. bus,
  1474. changeType(type){
  1475. //console.log('changeType', tag.title, type)
  1476. let onMesh = type == '3d'
  1477. if(tag.onMesh != onMesh){
  1478. tag.changeOnMesh(onMesh)
  1479. }
  1480. },
  1481. visibility(v){// 标注可见性
  1482. //console.log('visibility', tag.title, v)
  1483. Potree.Utils.updateVisible(tag,'user', v)
  1484. viewer.dispatchEvent('content_changed')
  1485. },
  1486. visibilityTitle(v){
  1487. tag.setTitleVisi(v, 'user')
  1488. },
  1489. changePosition({modelId,position,normal}){
  1490. let root = viewer.scene.pointclouds.concat(viewer.objs.children).find(e=>e.dataset_id == props.modelId)
  1491. tag.changePos({root,position,normal})
  1492. },
  1493. changeImage(url){
  1494. tag.changeMap(ossRoot + '/' +url)
  1495. },
  1496. changeTitle(title){
  1497. tag.setTitle(title)
  1498. },
  1499. changeMat({scale,rotation}){//大小旋转 贴墙时
  1500. tag.setFaceAngle(rotation)
  1501. tag.changeSpotScale(scale)
  1502. },
  1503. changeFontSize(fontsize){
  1504. tag.setFontSize(fontsize)
  1505. },
  1506. // 更改离地高度
  1507. changeLineHeight(height){//线长
  1508. tag.changeLineLen(height)
  1509. },
  1510. changeCanMove(canMove){
  1511. //console.log('changeCanMove', tag.title, canMove)
  1512. tag.dragEnable = canMove
  1513. },
  1514. getImageCenter(){ //热点在模型的本地坐标
  1515. if(!tag.parent)return new THREE.Vector3
  1516. tag.titleLabel.sprite.update()
  1517. tag.updateMatrixWorld() //有的不加不对
  1518. let pos = tag.onMesh ? tag.position : tag.titleLabel.parent.position.clone().applyMatrix4(tag.matrixWorld).applyMatrix4(tag.root.matrixWorld.clone().invert())
  1519. console.log(props.title, tag.id, 'getImageCenter', pos.toArray()/* , tag.titleLabel.parent.position.toArray(), */ )
  1520. return pos
  1521. },
  1522. getCameraDisSquared(){//距离intersect的位置
  1523. return viewer.mainViewport.camera.position.distanceToSquared(tag.getWorldPosition(new THREE.Vector3)) /* < tag.farSquared */
  1524. },
  1525. destory(){
  1526. tag.dispose()
  1527. },
  1528. }
  1529. tag.functions.changeImage(props.image)
  1530. /*
  1531. tag.functions.changeType(props.type)
  1532. */
  1533. return tag.functions
  1534. },
  1535. showGrid() {
  1536. Potree.Utils.updateVisible(viewer.modules.MergeEditor.ground, 'hideGrid', true)
  1537. viewer.dispatchEvent('content_changed')
  1538. },
  1539. hideGrid() {
  1540. Potree.Utils.updateVisible(viewer.modules.MergeEditor.ground, 'hideGrid', false)
  1541. viewer.dispatchEvent('content_changed')
  1542. }
  1543. }
  1544. function spliceFromArr(model, props, loaded){
  1545. //let autoLoads.find()
  1546. props.loadFinish = true
  1547. props.loading = false
  1548. if (loaded) {
  1549. props.loaded = true
  1550. props.model = model
  1551. } else {
  1552. props.error = true
  1553. }
  1554. /* let haventLoad = autoLoads.filter(e=>!e.loading && !e.loadFinish);
  1555. if( haventLoad[0]){
  1556. startLoad(haventLoad[0])
  1557. */
  1558. if (!loadNext()) {
  1559. if (autoLoads.filter(e => !e.loadFinish).length == 0 && autoLoads.filter(e => e.loaded).length > 0 && !props.isFirstLoad) {//设置相机位置:当自动开始加载第一个模型时(其余的也跟着自动加载),等这批加载完后;
  1560. let autoLoadsDone = autoLoads.filter(e => e.loaded).map(e => e.model)
  1561. let loadTimeCost = Date.now() - loadStartTime
  1562. console.log('所有模型加载完毕, 耗时', parseInt(loadTimeCost) )
  1563. autoLoads.filter(e => e.loaded && e.show).forEach(e => e.model.visible = true)
  1564. MergeEditor.focusOn(autoLoadsDone, 1000, true, true)
  1565. autoLoads.length = 0
  1566. }
  1567. }
  1568. }
  1569. function loadNext(){
  1570. let haventLoad = autoLoads.filter(e => !e.loading && !e.loadFinish);
  1571. let loading = autoLoads.filter(e => e.loading);
  1572. let needLoad = haventLoad.slice(0, maxLoadingCount - loading.length)
  1573. needLoad.forEach(e => startLoad(e))
  1574. return haventLoad.length > 0
  1575. }
  1576. function startLoad(prop){
  1577. /* if(prop.raw.visible !== 1){//用于临时隐藏
  1578. setTimeout(()=>{
  1579. spliceFromArr(null, prop, false)
  1580. prop.bus.emit('loadError' )
  1581. },1)
  1582. return
  1583. } */
  1584. if(prop.loading || prop.loadFinish)return
  1585. Potree.Log(`--开始加载--`, { font: { color: '#f68' } });
  1586. console.log('id:', prop.id, ', title:', prop.title, ', filename:', Potree.Common.getNameFromURL(prop.url), ', type:', prop.type, prop)
  1587. prop.unlit = prop.renderType != 'normal'
  1588. prop.maximumScreenSpaceError = 70
  1589. prop.prefix = prop.raw.prefix
  1590. /* laserRoot != void 0 && (prop.prefix = laserRoot) //prefix for getdataset
  1591. //Potree.settings.urls.prefix = prop.prefix = '' */
  1592. Potree.addModel(prop, prop.done, prop.progressFun, prop.onError)
  1593. prop.loading = true
  1594. }
  1595. function buildMapFromProp(){
  1596. cesiumViewer.imageryLayers.removeAll();
  1597. mapProps.forEach(e=>{
  1598. let gaoDeImageryProvider = new Cesium.UrlTemplateImageryProvider({
  1599. url:e.url,
  1600. minimumLevel: 0,
  1601. maximumLevel: e.maximumLevel,
  1602. credit: new Cesium.Credit(e.name),
  1603. tilingScheme: new AmapMercatorTilingScheme(), //修改投影,从84->高德
  1604. crossOrigin: 'anonymous',
  1605. })
  1606. cesiumViewer.imageryLayers.addImageryProvider(gaoDeImageryProvider);
  1607. })
  1608. }
  1609. function buildMap(){
  1610. if (Potree.settings.showCesium && !window.cesiumViewer) {
  1611. viewer.backgroundOpacity = 0
  1612. //密钥
  1613. Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2ZGM2YzY0ZC1kNWE0LTRiYTgtYTkwNS1kYmJiODRjMWUwMmQiLCJpZCI6MjMzMTQ1LCJpYXQiOjE3MjI5OTUwNTB9.niqpkl6xOkQ2KeJjelyDDDydmSGqKXKb5cX2NyxSNAw'
  1614. window.cesiumViewer = new Cesium.Viewer('app', {
  1615. useDefaultRenderLoop: true,
  1616. requestRenderMode: true, //add 只有需要render时才会render,如tile加载完后、镜头移动后
  1617. animation: false,
  1618. baseLayerPicker: false,
  1619. fullscreenButton: false,
  1620. geocoder: false,
  1621. homeButton: false,
  1622. infoBox: false,
  1623. sceneModePicker: false,
  1624. selectionIndicator: false,
  1625. timeline: false,
  1626. navigationHelpButton: false,
  1627. //高德秘钥版 imageryProvider: new Cesium.AmapImageryProvider({key, mapStyle: 'normal'})
  1628. //报错 401 (Unauthorized) 的方法 https://blog.csdn.net/LBY_XK/article/details/121992641
  1629. //terrainShadows: Cesium.ShadowMode.DISABLED, //terrain地形 //自带的地图直接用84坐标
  1630. });
  1631. buildMapFromProp()
  1632. Potree.cesScreenshot = (w,h)=>{
  1633. console.log('cesScreenshot',w,h)
  1634. cesiumViewer.scene.canvas.style.width = w+'px'
  1635. cesiumViewer.scene.canvas.style.height = h+'px'
  1636. cesiumViewer.scene.canvas.style.visibility = 'hidden'
  1637. cesiumViewer.resize()
  1638. cesAspect = w/h
  1639. let deferred = $.Deferred();
  1640. updateMap(w/h)//hfov可能改变了需要update。
  1641. setTimeout(()=>{ //延迟是似乎还要做别的处理,否则立即截图的话可能得到绿色底图(俯视状态容易触发)
  1642. let oldMode = window.cesiumViewer._cesiumWidget._scene.requestRenderMode
  1643. window.cesiumViewer._cesiumWidget._scene.requestRenderMode = 0 //强制render,否则会黑屏
  1644. cesiumViewer.render();
  1645. let dataUrl = window.cesiumViewer.scene.canvas.toDataURL('image/png')
  1646. window.cesiumViewer._cesiumWidget._scene.requestRenderMode = oldMode
  1647. //Potree.Common.downloadFile(dataUrl, 'screenshot.png')
  1648. cesAspect = null
  1649. cesiumViewer.scene.canvas.style.width = ''
  1650. cesiumViewer.scene.canvas.style.height = ''
  1651. cesiumViewer.scene.canvas.style.visibility = ''
  1652. deferred.resolve(dataUrl)
  1653. },200) //时间短了容易黑屏
  1654. return deferred.promise()
  1655. }
  1656. }else{
  1657. buildMapFromProp()
  1658. }
  1659. updateMap()
  1660. }
  1661. function updateCamNear(type){// 有的漫游场景模型缩放的很小(0.1%),需要缩小near才能看见, 但会造成z-fighting, 离远了看大模型会闪烁
  1662. const min = 0.0001, max = 0.1
  1663. let near , bigScale = 0.2
  1664. if(Potree.settings.displayMode == 'showPanos'/* && type != 'cameraMove' */){
  1665. let currentModel = viewer.images360.currentPano.pointcloud
  1666. near = Potree.math.linearClamp(currentModel.scale.x, [0, 1], [min, max])
  1667. }else/* if(type == 'cameraMove') */{
  1668. //没有完美的解决方式,优先考虑选中的模型。否则优先考虑离得近的。 另外尽量用大的,因为缩很小的情况很少。
  1669. //虽然案例说应该看model.bound.size,但如果非场景模型,缩很小的话也不需要凑近看。
  1670. let allModels = viewer.objs.children.concat(viewer.scene.pointclouds)
  1671. if(allModels.length == 0)return
  1672. allModels.sort((a,b)=>{return a.scale.x - b.scale.x})
  1673. let minS = allModels[0].scale.x, maxS = allModels.pop().scale.x
  1674. let considerModel
  1675. if(minS>bigScale) near = max
  1676. else{
  1677. if(MergeEditor.selected){
  1678. considerModel = MergeEditor.selected
  1679. near = Potree.math.linearClamp(considerModel.scale.x, [0, bigScale], [min, max])
  1680. }else{
  1681. //allModels = allModels.filter() //写不下去了好难,就算了吧, 折中
  1682. near = Potree.math.linearClamp(minS, [0, bigScale], [max/4, max])
  1683. }
  1684. }
  1685. }
  1686. if(near != viewer.mainViewport.camera.near){
  1687. console.log('updateNear',near)
  1688. viewer.mainViewport.camera.near = near
  1689. viewer.mainViewport.camera.updateProjectionMatrix()
  1690. viewer.dispatchEvent('content_changed')
  1691. }
  1692. }
  1693. function updateCamFar(){
  1694. let expand = 1.1 //for label
  1695. Potree.settings.cameraFar = THREE.Math.clamp((viewer.bound.boundingBox.distanceToPoint(viewer.mainViewport.camera.position)+viewer.bound.boundSize.length() ) * expand , 10000, 100000000000)
  1696. }
  1697. function updateMap(){
  1698. if (Potree.settings.showCesium && Potree.settings.displayMode == 'showPointCloud') {
  1699. let camera = MergeEditor.split ? viewer.viewports.find(e=>e.name == 'top').camera : viewer.mainViewport.camera
  1700. let pPos = new THREE.Vector3(0, 0, 0).applyMatrix4(camera.matrixWorld);
  1701. let orientation
  1702. let toCes = (pos) => {
  1703. let xy = [pos.x, pos.y];
  1704. let height = pos.z;
  1705. let deg = viewer.transform.lonlatToLocal.inverse(xy) // toMap.forward(xy);
  1706. let cPos = Cesium.Cartesian3.fromDegrees(...deg, height);
  1707. //console.log('toCes',cPos,height) //数字过大如e35会崩溃
  1708. return cPos;
  1709. };
  1710. let cPos = toCes(pPos);
  1711. if(MergeEditor.split){
  1712. orientation = {
  1713. heading: Cesium.Math.toRadians(0.0), // 方向角
  1714. pitch: Cesium.Math.toRadians(-90.0), // 俯仰角
  1715. roll: 0.0 // 翻滚角
  1716. }
  1717. if(!cesiumViewer.camera.perpFrustum_){
  1718. cesiumViewer.camera.perpFrustum_ = cesiumViewer.camera.frustum
  1719. cesiumViewer.camera.frustum = new Cesium.OrthographicOffCenterFrustum({//OrthographicFrustum OrthographicOffCenterFrustum
  1720. left: -10000, // 左边界
  1721. right: 10000, // 右边界
  1722. bottom: -10000, // 下边界
  1723. top: 10000, // 上边界
  1724. near: 1.0, // 近裁剪面距离
  1725. far: 100000000.0, // 远裁剪面距离
  1726. })
  1727. }
  1728. cesiumViewer.camera.frustum.left = camera.left / camera.zoom
  1729. cesiumViewer.camera.frustum.right = camera.right / camera.zoom
  1730. cesiumViewer.camera.frustum.top = camera.top / camera.zoom
  1731. cesiumViewer.camera.frustum.bottom = camera.bottom / camera.zoom
  1732. }else{
  1733. cesiumViewer.camera.perpFrustum_ && (cesiumViewer.camera.frustum = cesiumViewer.camera.perpFrustum_, cesiumViewer.camera.perpFrustum_ = null) //恢复
  1734. //let pRight = new THREE.Vector3(600, 0, 0).applyMatrix4(camera.matrixWorld);
  1735. let pUp = new THREE.Vector3(0, 600, 0).applyMatrix4(camera.matrixWorld);
  1736. let pTarget = viewer.scene.view.getPivot();
  1737. let cUpTarget = toCes(pUp);
  1738. let cTarget = toCes(pTarget);
  1739. let cDir = Cesium.Cartesian3.subtract(cTarget, cPos, new Cesium.Cartesian3());
  1740. let cUp = Cesium.Cartesian3.subtract(cUpTarget, cPos, new Cesium.Cartesian3());
  1741. cDir = Cesium.Cartesian3.normalize(cDir, new Cesium.Cartesian3());
  1742. cUp = Cesium.Cartesian3.normalize(cUp, new Cesium.Cartesian3());
  1743. //console.log('ces', 'cPos', cPos, 'cDir',cDir, 'cUp', cUp)
  1744. orientation = {
  1745. direction: cDir,
  1746. up: cUp
  1747. }
  1748. let aspect = cesAspect || camera.aspect;
  1749. //console.log('updateMap', aspect)
  1750. if (aspect < 1) {
  1751. let fovy = Math.PI * (viewer.scene.getActiveCamera().fov / 180);
  1752. cesiumViewer.camera.frustum.fov = fovy;
  1753. } else {
  1754. let fovy = Math.PI * (viewer.scene.getActiveCamera().fov / 180);
  1755. let fovx = Math.atan(Math.tan(0.5 * fovy) * aspect) * 2
  1756. cesiumViewer.camera.frustum.fov = fovx;
  1757. }
  1758. }
  1759. cesiumViewer.camera.setView({
  1760. destination: cPos,
  1761. orientation
  1762. });
  1763. cesiumViewer.scene.globe.show = camera.position.z > 0 //在地面之下地球会闪烁,故隐藏
  1764. cesiumViewer.render(); //立即render,否则会和点云render不同步而错位
  1765. }//cesium测试沙盒 https://sandcastle.cesium.com/
  1766. }
  1767. class AmapMercatorTilingScheme extends Cesium.WebMercatorTilingScheme {
  1768. constructor(options) {
  1769. super(options)
  1770. let projection = new Cesium.WebMercatorProjection()
  1771. this._projection.project = function(cartographic, result) {
  1772. //WGS84转GCJ02坐标
  1773. /* result = gcoord.transform([
  1774. Cesium.Math.toDegrees(cartographic.longitude),
  1775. Cesium.Math.toDegrees(cartographic.latitude)
  1776. ], gcoord.WGS84, gcoord.GCJ02) */
  1777. result = AMapWith84__.wgs84ToAMap({
  1778. x: Cesium.Math.toDegrees(cartographic.longitude),
  1779. y: Cesium.Math.toDegrees(cartographic.latitude)
  1780. })
  1781. result = projection.project(new Cesium.Cartographic(Cesium.Math.toRadians(result.x),Cesium.Math.toRadians(result.y)))
  1782. return new Cesium.Cartesian2(result.x,result.y)
  1783. }
  1784. this._projection.unproject = function(cartesian, result) {
  1785. let cartographic = projection.unproject(cartesian)
  1786. //GCJ02转WGS84坐标
  1787. /* result = gcoord.transform([
  1788. Cesium.Math.toDegrees(cartographic.longitude),
  1789. Cesium.Math.toDegrees(cartographic.latitude)
  1790. ], gcoord.GCJ02, gcoord.WGS84) */
  1791. result = AMapWith84__.aMapToWgs84({
  1792. x: Cesium.Math.toDegrees(cartographic.longitude),
  1793. y: Cesium.Math.toDegrees(cartographic.latitude)
  1794. })
  1795. return new Cesium.Cartographic(Cesium.Math.toRadians(result.x),Cesium.Math.toRadians(result.y))
  1796. }
  1797. }
  1798. }//see : https://blog.csdn.net/hongxianqiang/article/details/140541555 cesium加载高德地图并纠偏
  1799. return sdk
  1800. }
  1801. /*
  1802. 暂定不同场景间的漫游点不能互通。虽然它们可能是摆放正确的,如果是组成一整个场景的话还是要打通……
  1803. 不互通的方法是设置pano.enable
  1804. 现在需要互通了。但是还需要设置neibgbours, 有点麻烦,暂时没写。
  1805. */
  1806. export default enter