SiteModel.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. import * as THREE from "../../../libs/three.js/build/three.module.js";
  2. import SplitScreen from "../../utils/SplitScreen"
  3. import {BuildingBox} from "./BuildingBox"
  4. import Common from "../../utils/Common.js";
  5. import {Images360} from '../Images360/Images360'
  6. const minFloorHeight = 0.5
  7. const ifDrawDatasetBound = true //显示一下数据集的tightBound线框
  8. var SiteModel = {
  9. entities:[], //所有实体
  10. buildings:[], //所有建筑父集
  11. meshGroup: new THREE.Object3D,
  12. init: function(){
  13. viewer.scene.scene.add(this.meshGroup)
  14. this.meshGroup.name = 'siteModel'
  15. this.SplitScreen = SplitScreen
  16. this.createHeightPull();
  17. if(Potree.settings.isTest && ifDrawDatasetBound){
  18. viewer.on('allLoaded',()=>{
  19. viewer.scene.pointclouds.forEach(pointcloud=>{
  20. let boxPoints = pointcloud.getUnrotBoundPoint();
  21. let boundingBox = new BuildingBox({
  22. name: '数据集tightBound_'+pointcloud.dataset_id,
  23. points: boxPoints,
  24. buildType : 'dataset',
  25. zMax: pointcloud.bound.max.z,
  26. zMin: pointcloud.bound.min.z,
  27. ifDraw:true
  28. })
  29. this.meshGroup.add(boundingBox)
  30. //boundingBox.markers.forEach(e=>e.visible = false)
  31. })
  32. })
  33. }
  34. if(Potree.settings.isOfficial){
  35. let lastPos = new THREE.Vector3
  36. let lastEntity
  37. viewer.addEventListener('camera_changed', e => {
  38. if(!this.entities.length || this.editing) return
  39. Common.intervalTool.isWaiting('sitemodelCameraInterval', ()=>{ //延时update,防止卡顿
  40. let currPos = viewer.scene.getActiveCamera().position
  41. if(!currPos.equals(lastPos)){
  42. lastPos.copy(currPos)
  43. let entity;
  44. if(Potree.settings.displayMode == 'showPanos'){
  45. entity = this.entities.find(e=>e.panos.includes(viewer.images360.currentPano))
  46. if(!entity)console.log('没找到entity')
  47. }
  48. if(!entity){
  49. entity = this.pointInWhichEntity(currPos, 'room');
  50. }
  51. if(lastEntity != entity ){
  52. console.log('buildingChange', entity)
  53. entity && Potree.sdk.scene.emit('buildingChange', entity.polygon)
  54. lastEntity = entity
  55. }
  56. return true
  57. }
  58. }, 1000)
  59. })
  60. }
  61. },
  62. enter:function(){
  63. Potree.Log('sitemodel enter')
  64. this.clear() //确保全部清空
  65. this.editing = true
  66. let mapViewport = viewer.mapViewer.viewports[0]
  67. SplitScreen.splitScreen4Views({siteModel:true/* , viewports:[{name:'Top',viewport : mapViewport }] */})
  68. viewer.viewports.forEach(e=>{
  69. if(e.name != 'mapViewport'){
  70. e.layersAdd('siteModelMapUnvisi')
  71. }
  72. if(e.name == 'Right' || e.name == 'Back'){
  73. e.layersAdd('siteModeSideVisi')
  74. }
  75. })
  76. viewer.images360.panos.forEach(pano=>{
  77. viewer.setObjectLayers(pano.marker, 'siteModelMapUnvisi' )
  78. })
  79. mapViewport.layersAdd('siteModeOnlyMapVisi') //只有mapViewport能看到marker
  80. },
  81. leave:function(){
  82. Potree.Log('sitemodel leave')
  83. let mapViewport = viewer.mapViewer.viewports[0]
  84. SplitScreen.recoverFrom4Views()
  85. viewer.images360.panos.forEach(pano=>{
  86. viewer.setObjectLayers(pano.marker, 'mapObjects' )
  87. })
  88. mapViewport.layersRemove('siteModeOnlyMapVisi')
  89. this.clear()
  90. this.editing = false
  91. }
  92. ,
  93. /* startSetSiteModel:function(pos, type){//开始创建空间模型(非编辑状态的,不绘制)
  94. if(this.editing)return //编辑中不允许重新创建
  95. this.clear()
  96. }, */
  97. addFloor:function(parent, dirType, sid, name){//dirType:'top'|'bottom'在上方建还是下方。如果建筑中没有楼层,默认在基底建一个
  98. let buildType = 'floor'
  99. let zMin, zMax
  100. if(parent.buildChildren.length == 0){
  101. zMin = parent.zMin
  102. zMax = zMin + Potree.config.siteModel.floorHeightDefault
  103. }else{
  104. if(dirType == 'bottom'){
  105. //var btm = Common.find(parent.buildChildren,null,[e=>e.zMin])
  106. var btm = parent.buildChildren[0]
  107. zMax = btm.zMin
  108. zMin = zMax - Potree.config.siteModel.floorHeightDefault
  109. }else{
  110. //var top = Common.find(parent.buildChildren,null,[e=>e.zMax])
  111. var top = parent.buildChildren[parent.buildChildren.length - 1]
  112. zMin = top.zMax
  113. zMax = zMin + Potree.config.siteModel.floorHeightDefault
  114. }
  115. }
  116. let prop = {
  117. buildType,
  118. //name : Potree.config.siteModel.names[buildType],
  119. zMin,
  120. zMax,
  121. buildParent:parent,
  122. sid, name,
  123. ifDraw:true
  124. }
  125. var floor = new BuildingBox(prop);
  126. /* parent.buildChildren.push(floor)
  127. this.meshGroup.add(floor);
  128. this.entities.push(floor) */
  129. floor.update()
  130. this.addEntity(floor,parent)
  131. this.selectEntity(floor)
  132. this.updateBuildingZ(parent)
  133. return floor
  134. },
  135. startInsertion:function(buildType, parent, sid, name, callback, cancelFun){
  136. let zMin, zMax
  137. if(buildType == 'hole' || buildType == 'room'){
  138. zMin = parent.zMin
  139. zMax = parent.zMax
  140. }else if(buildType == 'building'){
  141. parent = null
  142. zMin = viewer.bound.boundingBox.min.z
  143. zMax = viewer.bound.boundingBox.min.z
  144. }
  145. let minMarkers = 3
  146. let mapViewport = viewer.mapViewer.viewports[0]
  147. let entity
  148. if(buildType == 'hole'){
  149. entity = parent.addHole()
  150. this.selectEntity(parent)
  151. entity.select()
  152. console.log('挖洞 ',entity.uuid)
  153. }else{
  154. let prop = {
  155. buildType,
  156. //name : Potree.config.siteModel.names[buildType],//'building',
  157. zMin,
  158. zMax,
  159. buildParent:parent,
  160. sid, name,
  161. ifDraw:true
  162. }
  163. entity = new BuildingBox(prop);
  164. this.selectEntity(entity)
  165. }
  166. entity.isNew = true
  167. let timer;
  168. let endDragFun = (e) => {
  169. if (e.button == THREE.MOUSE.LEFT ) {
  170. var marker = entity.addMarker({point:entity.points[entity.points.length - 1].clone()})
  171. //entity.editStateChange(true) //重新激活reticule状态
  172. entity.continueDrag(marker, e)
  173. } else if (e.button === THREE.MOUSE.RIGHT ) {
  174. if(e.pressDistance < Potree.config.clickMaxDragDis )end(e);//非拖拽的话
  175. else entity.continueDrag(null, e/* .drag.object */)
  176. }
  177. };
  178. let end = (e={}) => {//确定、结束
  179. if(!e.finish && entity.markers.length<=minMarkers){//右键 当个数不够时取消
  180. //重新开始画
  181. entity.reDraw(1)
  182. viewer.updateVisible(entity.markers[0],'unMove',false);
  183. var f = ()=>{
  184. viewer.updateVisible(entity.markers[0],'unMove',true);
  185. entity.removeEventListener('dragChange',f)
  186. }
  187. entity.addEventListener('dragChange',f)
  188. console.log('waitcontinue')
  189. entity.continueDrag(entity.markers[0], e)
  190. return
  191. }
  192. viewer.removeEventListener('cancel_insertions', Exit);
  193. //entity.removeEventListener('unselect', Exit);
  194. clearTimeout(timer)
  195. entity.editStateChange(false)
  196. if (!e.finish && entity.markers.length > 3) {
  197. entity.removeMarker(entity.points.length - 1);
  198. entity.addHoverEvent()
  199. if(buildType == 'room'){
  200. this.fitPullBox()
  201. }
  202. entity.isNew = false
  203. entity.addMidMarkers()
  204. pressExit && viewer.inputHandler.removeEventListener('keydown', pressExit);
  205. callback && callback(entity)
  206. }else{
  207. this.removeEntity(entity) //直接删除没画好的,比较简单。这样就不用担心旧的continueDrag仍旧触发了
  208. }
  209. return entity
  210. };
  211. let Exit = (e)=>{
  212. //entity.removeEventListener('unselect', Exit);
  213. if(viewer.inputHandler.drag){//还未触发drop的话
  214. viewer.inputHandler.drag.object.dispatchEvent({
  215. type: 'drop',
  216. drag: viewer.inputHandler.drag,
  217. viewer: viewer,
  218. pressDistance:0,
  219. button : THREE.MOUSE.RIGHT
  220. });
  221. viewer.inputHandler.drag = null
  222. }else{
  223. end({finish:true, remove:e.remove}) //未结束时添加新的polygon时会触发
  224. }
  225. viewer.inputHandler.drag = null
  226. }
  227. viewer.dispatchEvent({
  228. type: 'cancel_insertions' //取消之前的
  229. });
  230. viewer.addEventListener('cancel_insertions', Exit);
  231. //entity.addEventListener('unselect', Exit); //这个太难了,创建时也会被取消选中的
  232. let pressExit
  233. if(!Potree.settings.isOfficial){
  234. pressExit = (e)=>{
  235. if(e.keyCode == 27){//Esc
  236. Exit()
  237. }
  238. }
  239. viewer.inputHandler.addEventListener('keydown', pressExit)
  240. }
  241. var marker = entity.addMarker({point:new THREE.Vector3(0, 0, 0)})
  242. viewer.inputHandler.startDragging(marker , {dragViewport:mapViewport, endDragFun, notPressMouse:true} ); //notPressMouse代表不是通过按下鼠标来拖拽. dragViewport指定了只能在地图上拖拽
  243. viewer.updateVisible(marker,'unMove',false);//这时候的位置是假的(0,0,0)所以先不可见
  244. var f = ()=>{
  245. viewer.updateVisible(marker,'unMove',true);
  246. entity.removeEventListener('dragChange',f)
  247. }
  248. entity.addEventListener('dragChange',f)
  249. if(buildType!='hole'){
  250. this.addEntity(entity, parent)
  251. }
  252. return entity;
  253. },
  254. createFromData:function( buildType, parent ,sid, name, points=[], holes=[], zMin, zMax, initial,panos,flagPano){
  255. if(buildType != 'building' && buildType != 'floor' && buildType != 'room' ) return
  256. {
  257. if(initial){//初始数据错的,要自己建(只有一个building和floor) 原posIsLonlat
  258. var bound = viewer.bound.boundingBox
  259. points = [
  260. new THREE.Vector3(bound.min.x, bound.min.y,0),
  261. new THREE.Vector3(bound.max.x, bound.min.y,0),
  262. new THREE.Vector3(bound.max.x, bound.max.y,0),
  263. new THREE.Vector3(bound.min.x, bound.max.y,0),
  264. ]
  265. zMin = bound.min.z
  266. zMax = bound.max.z
  267. /* points = points.map(e=>{
  268. return viewer.transform.lonlatToLocal.forward(e)
  269. }) */
  270. }else{//相对于初始数据集的模型内坐标
  271. points = points.map(e=> this.transform(e, 'fromDataset'))
  272. }
  273. }
  274. if(buildType == 'building' ){
  275. zMax = zMin //强制变得一样,作为基底。如果有必要,保存时再算真实的zMax。目前zMin没有保存所以数据是错的,会直接根据floor计算
  276. }
  277. {
  278. let getPano = (id)=>{
  279. return viewer.images360.panos.find(pano=>pano.id == id)
  280. }
  281. panos = panos ? panos.map(e=>getPano(e)) : [];
  282. flagPano = flagPano != void 0 ? getPano(flagPano) : null ; //最中心的pano 或者 最靠近该实体的pano(当panos为空时)
  283. if(!this.editing && buildType == 'floor' && !flagPano){//没有的话可能是自动添加的floor,直接用parent的吧
  284. panos = parent.panos;
  285. flagPano = parent.flagPano;
  286. }
  287. }
  288. let prop = {
  289. buildType,
  290. points,
  291. name,
  292. sid,
  293. zMin,
  294. zMax,
  295. buildParent:parent,
  296. ifDraw:this.editing || Potree.settings.drawEntityData,
  297. panos,flagPano
  298. }
  299. let entity = new BuildingBox(prop)
  300. SiteModel.addEntity(entity, parent )
  301. if(this.editing){
  302. if(buildType == 'building'|| buildType == 'room'){
  303. entity.addMidMarkers()
  304. }
  305. }
  306. holes.forEach(points =>{
  307. let ps = points.map(e=> this.transform(e, 'fromDataset'))
  308. let hole = entity.addHole(ps)
  309. this.editing && hole.addMidMarkers()
  310. })
  311. if(buildType == 'floor'){
  312. this.updateBuildingZ(parent)
  313. }
  314. return entity
  315. },
  316. transform:function(pos, type){
  317. if(type == 'toDataset'){
  318. let point = Potree.Utils.datasetPosTransform({ toDataset: true, position: pos.clone(), datasetId: Potree.datasetData[0].id })
  319. return new THREE.Vector2().copy(point)
  320. }else{
  321. let position = new THREE.Vector3().copy(pos).setZ(0)
  322. return Potree.Utils.datasetPosTransform({ fromDataset: true, position, datasetId: Potree.datasetData[0].id })
  323. }
  324. },
  325. addEntity:function(entity, parent){
  326. this.meshGroup.add(entity);
  327. this.entities.push(entity)
  328. if(entity.buildType == 'building'){
  329. this.buildings.push(entity)
  330. }else{
  331. parent.buildChildren.push(entity)
  332. }
  333. if(entity.buildType == 'room'){
  334. entity.addEventListener('marker_dropped',()=>{
  335. this.fitPullBox()
  336. })
  337. }else if(entity.buildType == 'floor'){
  338. parent.dispatchEvent({type:'addFloor'})
  339. }
  340. console.log('添加实体:', entity.buildType, entity.sid, entity.uuid)
  341. },
  342. removeEntity : function(entity){
  343. if(!this.entities.includes(entity))return
  344. if(this.selected == entity){
  345. this.height_pull_box.visible = false
  346. this.selectEntity(null)
  347. }
  348. if(entity.buildType == 'building'){
  349. var index = this.buildings.indexOf(entity);
  350. if(index>-1){
  351. this.buildings.splice(index,1)
  352. }
  353. }else{
  354. var index = entity.buildParent.buildChildren.indexOf(entity);
  355. if(index>-1){
  356. entity.buildParent.buildChildren.splice(index,1)
  357. }
  358. }
  359. var index = this.entities.indexOf(entity);
  360. if(index>-1){
  361. this.entities.splice(index,1)
  362. }
  363. entity.dispose()
  364. entity.dispatchEvent({type:'delete'})
  365. console.log('删除实体:', entity.buildType, entity.sid)
  366. },
  367. updateBuildingZ:function(building){
  368. building.buildChildren = building.buildChildren.sort((e,a)=>e.zMin-a.zMin)//从低到高排序
  369. building.zMin = building.zMax = building.buildChildren[0].zMin //基底高度
  370. //building.zMax = building.buildChildren[building.buildChildren.length-1].zMax
  371. if(this.editing) building.update({dontUpdateChildren:true})
  372. },
  373. selectEntity : function(entity){
  374. if(this.selected == entity || entity && entity.buildType == 'hole')return
  375. //this.buildings.forEach(e=>e.unselect())
  376. this.selected && this.selected.unselect()
  377. this.height_pull_box.visible = false
  378. if(entity){
  379. entity.select()
  380. }
  381. this.selected = entity
  382. if(entity && (entity.buildType == 'floor' || entity.buildType == 'room' )){
  383. this.height_pull_box.visible = true
  384. this.fitPullBox()
  385. }
  386. },
  387. /* selectFloor:function(floor){
  388. this.buildings.forEach(e=>e.unselect())
  389. floor.select()
  390. this.selected = floor
  391. this.height_pull_box.visible = true
  392. this.fitPullBox()
  393. },
  394. selectBuilding:function(building){
  395. this.buildings.forEach(e=>e.unselect())
  396. building.select()
  397. }
  398. selectRoom:function(room){
  399. this.buildings.forEach(e=>e.unselect())
  400. room.select()
  401. }
  402. */
  403. fitPullBox: function(){ //自适应拖拽楼层的pullMesh
  404. let bound = new THREE.Box3();
  405. bound.expandByObject(this.selected.box)
  406. let center = bound.getCenter(new THREE.Vector3() )
  407. let size = bound.getSize(new THREE.Vector3() )
  408. this.height_pull_box.scale.copy(size)
  409. this.height_pull_box.position.copy(center)
  410. },
  411. changeZ:function(entity, dirType, value){ // floor or room 修改zMin or zMax
  412. let max, min //limit
  413. if(entity.buildType == 'floor'){//楼层
  414. let index = entity.buildParent.buildChildren.indexOf(this.selected)
  415. if(dirType == 'zMax'){
  416. let upper = entity.buildParent.buildChildren[index+1];
  417. entity.zMax = value
  418. min = entity.zMin + minFloorHeight
  419. if(entity.zMax < min){
  420. entity.zMax = min
  421. }else{
  422. if(upper){
  423. max = upper.zMax - minFloorHeight;
  424. if(entity.zMax > max){
  425. entity.zMax = max
  426. }
  427. }
  428. }
  429. if(upper){
  430. upper.zMin = entity.zMax
  431. upper.update()
  432. upper.dispatchEvent({type:'changeHeight'})
  433. }
  434. }else{
  435. let lower = entity.buildParent.buildChildren[index-1];
  436. entity.zMin = value
  437. max = entity.zMax - minFloorHeight
  438. if(entity.zMin > max){
  439. entity.zMin = max
  440. }else{
  441. if(lower){
  442. min = lower.zMin + minFloorHeight;
  443. if(entity.zMin < min){
  444. entity.zMin = min
  445. }
  446. }
  447. }
  448. if(lower){
  449. lower.zMax = entity.zMin
  450. lower.update()
  451. lower.dispatchEvent({type:'changeHeight'})
  452. }
  453. if(index == 0)this.updateBuildingZ(this.selected.buildParent)
  454. }
  455. }else if(entity.buildType == 'room'){//房间
  456. //按照navvis的是不一定限制在当前楼层,只要高度不超过当前楼层即可。
  457. let maxHeight = entity.buildParent.zMax - entity.buildParent.zMin
  458. if(dirType == 'zMax'){
  459. min = entity.zMin + minFloorHeight
  460. max = entity.zMin + maxHeight
  461. entity.zMax = THREE.Math.clamp(value, min, max);
  462. }else{
  463. min = entity.zMax - maxHeight
  464. max = entity.zMax - minFloorHeight
  465. entity.zMin = THREE.Math.clamp(value, min, max);
  466. }
  467. }
  468. entity.update()
  469. entity.dispatchEvent({type:'changeHeight'})
  470. //this.selected.emit('update')
  471. this.fitPullBox()
  472. },
  473. createHeightPull:function(){ //拖拽楼层的bounding box
  474. let boxGeo = new THREE.BoxBufferGeometry( 1, 1, 1/4 )
  475. let boxMat = new THREE.MeshBasicMaterial({
  476. color:"#F00",
  477. opacity:0 ,
  478. transparent:true,
  479. depthTest:false
  480. })
  481. let height_pull_box_up = new THREE.Mesh(boxGeo,boxMat)
  482. let height_pull_box_down = new THREE.Mesh(boxGeo,boxMat)
  483. height_pull_box_up.name = 'height_pull_box_up';
  484. height_pull_box_down.name = 'height_pull_box_down';
  485. this.height_pull_box = new THREE.Object3D();
  486. this.height_pull_box.name = 'height_pull_box'
  487. this.height_pull_box.add(height_pull_box_up)
  488. this.height_pull_box.add(height_pull_box_down)
  489. this.height_pull_box.visible = false
  490. this.meshGroup.add(this.height_pull_box)
  491. height_pull_box_up.position.set(0,0,3/8)
  492. height_pull_box_down.position.set(0,0,-3/8)
  493. viewer.setObjectLayers(this.height_pull_box, 'siteModeSideVisi' )
  494. let mouseover = (e)=>{
  495. viewer.dispatchEvent({
  496. type : "CursorChange", action : "add", name:"siteModelFloorDrag"
  497. })
  498. }
  499. let mouseleave = (e)=>{
  500. viewer.dispatchEvent({
  501. type : "CursorChange", action : "remove", name:"siteModelFloorDrag"
  502. })
  503. }
  504. let firstZ, firstIntersect;
  505. let drag = (e)=>{
  506. var intersectPoint = e.intersectPoint.orthoIntersect //不要点云的intersect,只要orthocamera算出的平面intersect
  507. if(firstIntersect != void 0){
  508. let moveZ = intersectPoint.z - firstIntersect
  509. if(this.selected.buildType == 'floor'){//楼层
  510. //限制高度不能超过上下
  511. if(e.target == height_pull_box_up){
  512. if(firstZ == void 0)firstZ = this.selected.zMax
  513. this.changeZ(this.selected, 'zMax', firstZ + moveZ)
  514. }else{
  515. if(firstZ == void 0)firstZ = this.selected.zMin
  516. this.changeZ(this.selected, 'zMin', firstZ + moveZ)
  517. }
  518. }else if(this.selected.buildType == 'room'){//房屋
  519. if(e.target == height_pull_box_up){
  520. if(firstZ == void 0)firstZ = this.selected.zMax
  521. this.changeZ(this.selected, 'zMax', firstZ + moveZ)
  522. }else{
  523. if(firstZ == void 0)firstZ = this.selected.zMin
  524. this.changeZ(this.selected, 'zMin', firstZ + moveZ)
  525. }
  526. }
  527. }else{
  528. firstIntersect = intersectPoint.z
  529. }
  530. }
  531. let drop = (e)=>{
  532. firstZ = firstIntersect = null
  533. }
  534. height_pull_box_up.addEventListener('mousemove',mouseover)
  535. height_pull_box_down.addEventListener('mousemove',mouseover)
  536. height_pull_box_up.addEventListener('mouseleave',mouseleave)
  537. height_pull_box_down.addEventListener('mouseleave',mouseleave)
  538. height_pull_box_up.addEventListener('drag',drag)
  539. height_pull_box_down.addEventListener('drag',drag)
  540. height_pull_box_up.addEventListener('drop',drop)
  541. height_pull_box_down.addEventListener('drop',drop)
  542. },
  543. pointInWhichEntity(position, buildType, ifIgnoreHole){//返回第一个符合标准的实体,buildType是要找的建筑类型
  544. //由于房间可能在building外,所以房间要另外单独识别。
  545. let lastResult; //最接近的上一层结果,如果没有result返回这个
  546. let result
  547. let traverse = (parent)=>{
  548. if(parent.ifContainsPoint(position)){
  549. lastResult = parent
  550. if(parent.buildType == buildType){
  551. return parent
  552. }else{
  553. for(let i=0,len=parent.buildChildren.length; i<len; i++){
  554. if(traverse(parent.buildChildren[i])){
  555. return parent.buildChildren[i]
  556. }
  557. }
  558. }
  559. }
  560. }
  561. for(let i=0,len=this.buildings.length; i<len; i++){
  562. result = traverse(this.buildings[i])
  563. if(result){break}
  564. }
  565. /* if(!result && buildType == 'room'){//如果要找的是room, 且按刚才的顺序找不到的话,就单独从所有rooms中找一遍。因为room可能不在floor和building内。
  566. let rooms = this.entities.filter(e=>e.buildType == 'room');
  567. result = rooms.find(e=>e.ifContainsPoint(position))
  568. }*/
  569. //虽然房间可以画到上级之外,但是为了方便起见,假定房间绝对在楼层之内。找不到的话要调整空间模型了。
  570. return result || lastResult
  571. }
  572. ,
  573. findPanos: function(){
  574. viewer.images360.panos.forEach(pano=>{
  575. let result = this.pointInWhichEntity(pano.position, 'room');
  576. {//get panos for every entities
  577. let entity = result
  578. while(entity){
  579. entity.panos.push(pano);
  580. entity = entity.buildParent
  581. }
  582. }
  583. })
  584. {//search center pano
  585. this.entities.forEach(entity=>{
  586. let bound = entity.getBound();
  587. let center = bound.getCenter(new THREE.Vector3)
  588. let request = []
  589. let rank = [
  590. Images360.scoreFunctions.distanceSquared({position: center})
  591. ]
  592. let panos = entity.panos && entity.panos.length ? entity.panos : viewer.images360.panos
  593. let r = Common.sortByScore(panos, request, rank);//entity没有panos的话,就扩大到所有panos
  594. if(r && r.length){
  595. entity.flagPano = r[0].item
  596. }else{
  597. console.error('no flagPano??')
  598. }
  599. })
  600. }
  601. }
  602. ,
  603. findEntityForDataset:function(){
  604. var entities = this.entities.filter(e=>e.buildType == 'room' || e.buildType == 'floor' && e.buildChildren.length == 0)
  605. entities.length && viewer.scene.pointclouds.forEach(pointcloud=>{
  606. let volumes = []
  607. entities.forEach(entity=>{
  608. let volume = entity.intersectPointcloudVolume(pointcloud)
  609. volumes.push({entity, volume})
  610. })
  611. volumes.sort((a,b)=>{ return b.volume-a.volume })
  612. console.log(volumes)
  613. pointcloud.belongToEntity = volumes[0].entity; //如果约等于0怎么办???
  614. })
  615. /*
  616. 只需要考虑 floor 和 room, 因为building的只有一个基底没高度
  617. floor 和 room 在空间中没有完全的从属关系,因为room可以超出floor之外。所以直接混在一起来查找,但要排除有房间的楼层。
  618. 直接计算实体和点云的重叠体积 ,重叠体积大的最小实体将会拥有该点云。
  619. */
  620. }
  621. ,
  622. clear:function(){//清空
  623. /* entities:[], //所有实体
  624. buildings:[], //所有建筑父集
  625. meshGroup: new THREE.Object3D, */
  626. this.selectEntity(null)
  627. let length = this.buildings.length;
  628. for(let i=0;i<length;i++){
  629. this.buildings[i].dispose()
  630. }
  631. this.entities = []
  632. this.buildings = []
  633. }
  634. ,
  635. gotoEntity(id, isNearBy) {
  636. var entity = this.entities.find(e => e.sid == id)
  637. let aimPano
  638. if (!entity) {
  639. return console.error('没找到entity ')
  640. }
  641. if (isNearBy && entity.panos.length) {
  642. let position = viewer.scene.getActiveCamera().position
  643. let request = []
  644. let rank = [Images360.scoreFunctions.distanceSquared({ position })]
  645. let r = Common.sortByScore(entity.panos, request, rank)
  646. aimPano = r[0].item
  647. } else {
  648. if (!entity.flagPano) {
  649. return console.error('没找到flagPano')
  650. }
  651. aimPano = entity.flagPano
  652. }
  653. viewer.images360.flyToPano(aimPano)
  654. },
  655. }
  656. /*
  657. 规则
  658. 层级:
  659. type 中文名 改动范围 其他
  660. BUILDING 建筑 xy mesh由自己的基底以及所有floor的组成。如果删除所有floor,就剩一个平面。故而zMin == zMax
  661. FLOOR 楼层 z(xy未解锁) 点击楼层时房间也会显示,而建筑的其他楼层不显示线框,会显示面. 拖拽高度实际是拖拽楼层间的分界线,楼层之间不会有缝隙
  662. ROOM 房间 xyz(xy可加锁) 可能超出楼层外,因为楼层拖拽时房间没变。所以建筑不一定包容房间。
  663. CUSTOM 自定义(现作房间) xyz
  664. ( xy范绘制时不能超出父级外轮廓,但父级编辑时可以进入子级轮廓。只要能解锁轮廓的都能切洞)
  665. floor输入高度数值的限制和拖拽是一样的,相当于调节其zMax, 且不能超出其上层的zMax。
  666. navvis弊端:
  667. 空间模型不会随着数据集移动而移动 (可以做成跟随,但是如果一个建筑对应多个数据集,那只能跟序号在前的数据集走)
  668. 建筑点修改后,房间可能飘出建筑外的。 楼层高度修改后也是。
  669. 调整高度时,看不到相邻的楼层界限,导致拖不动时像bug。
  670. 调整高度时,侧面看有的重叠的部分比较高亮,感觉是冗余信息?有点乱
  671. 问题:
  672. 磨砂材质
  673. 没有阴影,可directionallight 加了呀
  674. 暂定一个数据集只属于一个实体(从最小的找起)
  675. https://testlaser.4dkankan.com/indoor/t-8KbK1JjubE/api/site_model
  676. 删点全删光了要删实体吗
  677. */
  678. export {SiteModel}