123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260 |
- import * as THREE from "../../../libs/three.js/build/three.module.js";
- import SplitScreen from "../../utils/SplitScreen"
- import {BuildingBox} from "./BuildingBox"
- import Common from "../../utils/Common.js";
- import {Images360} from '../Images360/Images360'
- import {KeyCodes} from '../../KeyCodes'
- import {config } from "../../settings.js";
- import math from "../../utils/math.js";
- const minFloorHeight = 0.5
- const ifDrawDatasetBound = true //显示一下数据集的tightBound线框
- const minMarkers = 3
- const Limit = {zMin:-config.map.cameraHeight, zMax:config.map.cameraHeight,} //不能超过camera的高度,为了对称所以也限制了最低
- var SiteModel = {
- bus: new THREE.EventDispatcher(),
- entities:[], //所有实体
- buildings:[], //所有建筑父集
- meshGroup: new THREE.Object3D,
- inEntity : null,
- lastPos: new THREE.Vector3(Infinity,Infinity,Infinity),
-
-
- init: function(){
-
-
- viewer.scene.scene.add(this.meshGroup)
- this.meshGroup.name = 'siteModel'
- this.SplitScreen = SplitScreen
-
- if(Potree.settings.editType == 'pano'){
- return
- }
-
-
-
-
- this.createHeightPull();
-
- if(Potree.settings.isTest && ifDrawDatasetBound){
- viewer.addEventListener('allLoaded',()=>{
-
- viewer.scene.pointclouds.forEach(pointcloud=>{
- let boxPoints = pointcloud.getUnrotBoundPoint();
-
- let boundingBox = new BuildingBox({
- name: '数据集tightBound_'+pointcloud.dataset_id,
- points: boxPoints,
- buildType : 'dataset',
- zMax: pointcloud.bound.max.z,
- zMin: pointcloud.bound.min.z,
- ifDraw:true
- })
-
- this.meshGroup.add(boundingBox)
- //boundingBox.markers.forEach(e=>e.visible = false)
- })
- })
- }
- if(Potree.settings.isOfficial){
-
- viewer.addEventListener('camera_changed', e => {
- this.updateEntityAt()
- })
- }
-
-
- {
- let pressDelete = (e)=>{
- if(e.keyCode == KeyCodes.BACKSPACE || e.keyCode == KeyCodes.DELETE){
- if(this.selectedMarker){
- let entity = this.selectedMarker.parent
- let index = entity.markers.indexOf(this.selectedMarker)
- entity.removeMarker(index)
-
- if(entity.points.length<2){//删到只剩一个点时重新画(如果是hole的点,直接删除hole吧?)
- this.startInsertion('resume',entity)
- }
- }
- }
- }
- viewer.inputHandler.addEventListener('keydown', pressDelete)
-
-
- }
-
-
- },
-
-
-
- updateEntityAt(force){
- //if(!this.entities.length || this.editing) return //编辑时也要根据位置显示不同楼层的漫游点与cad
-
- let fun = ()=>{ //延时update,防止卡顿
- let currPos = viewer.mainViewport.view.position
-
- if(force || !currPos.equals(this.lastPos)){
- //console.log('currPos ', currPos.toArray())
- this.lastPos.copy(currPos)
- let entity;
-
- let searchPos = Potree.settings.displayMode == 'showPanos' ? viewer.images360.currentPano : currPos
- entity = this.pointInWhichEntity(searchPos, 'room');
-
- if(force || this.inEntity != entity ){
- let oldEntity = this.inEntity
- this.inEntity = entity
- console.log('buildingChange', entity)
- this.bus.dispatchEvent({type:'buildingChange',entity})
- //this.updatePanosVisible(oldEntity, this.inEntity)
-
- let lastFloor = this.currentFloor //oldEntity ? oldEntity.buildType == 'floor' ? oldEntity : oldEntity.buildType == 'room' ? oldEntity.buildParent : null : null; //基本只会是floor或room
- let currentFloor = entity ? entity.buildType == 'floor' ? entity : entity.buildType == 'room' ? entity.buildParent : null : null; //基本只会是floor或room
- if(currentFloor != lastFloor || force){
- console.log('改变了floor',lastFloor,currentFloor)
- this.currentFloor = currentFloor
- this.bus.dispatchEvent({type:'FloorChange',currentFloor})
- }
-
- }
- force = false
- return true
- }
- }
-
- if(force)fun()
- else Common.intervalTool.isWaiting('sitemodelCameraInterval', fun , 500)
-
-
-
- },
-
- enter:function(){
-
- Potree.Log('sitemodel enter')
- this.clear() //确保全部清空
- this.editing = true
- //this.updatePanosVisible(null, null, true)//show all
- viewer.updateFpVisiDatasets()
-
-
- let mapViewport = viewer.mapViewer.viewports[0]
- SplitScreen.splitScreen4Views({siteModel:true/* , viewports:[{name:'Top',viewport : mapViewport }] */})
-
-
- viewer.viewports.forEach(e=>{
- if(e.name != 'mapViewport'){
- e.layersAdd('siteModelMapUnvisi')
- }
- if(e.name == 'right' || e.name == 'back'){
- e.layersAdd('siteModeSideVisi')
- }
- })
-
-
- viewer.images360.panos.forEach(pano=>{
- viewer.setObjectLayers(pano.marker, 'siteModelMapUnvisi' )
- })
- mapViewport.layersAdd('siteModeOnlyMapVisi') //只有mapViewport能看到marker
-
-
-
- },
-
-
-
- leave:function(){
-
- Potree.Log('sitemodel leave')
-
- let mapViewport = viewer.mapViewer.viewports[0]
- SplitScreen.recoverFrom4Views()
- viewer.viewports.forEach(e=>{
- if(e.name != 'mapViewport'){
- e.layersRemove('siteModelMapUnvisi')
- }
- if(e.name == 'right' || e.name == 'back'){
- e.layersRemove('siteModeSideVisi')
- }
- })
- viewer.images360.panos.forEach(pano=>{
- viewer.setObjectLayers(pano.marker, 'sceneObjects' )
- })
-
- mapViewport.layersRemove('siteModeOnlyMapVisi')
- this.clear()
- this.editing = false
-
- viewer.updateFpVisiDatasets()
- } ,
-
-
-
-
-
- addFloor:function(parent, dirType, sid, name){//dirType:'top'|'bottom'在上方建还是下方。如果建筑中没有楼层,默认在基底建一个
- let buildType = 'floor'
- let zMin, zMax
- if(parent.buildChildren.length == 0){
- zMin = parent.zMin
- zMax = zMin + Potree.config.siteModel.floorHeightDefault
- }else{
- if(dirType == 'bottom'){
- //var btm = Common.find(parent.buildChildren,null,[e=>e.zMin])
- var btm = parent.buildChildren[0]
- zMax = btm.zMin
- zMin = zMax - Potree.config.siteModel.floorHeightDefault
- }else{
- //var top = Common.find(parent.buildChildren,null,[e=>e.zMax])
- var top = parent.buildChildren[parent.buildChildren.length - 1]
- zMin = top.zMax
- zMax = zMin + Potree.config.siteModel.floorHeightDefault
- }
-
- }
-
-
- let prop = {
- buildType,
- //name : Potree.config.siteModel.names[buildType],
- zMin,
- zMax,
- buildParent:parent,
- sid, name,
- ifDraw:true
- }
- var floor = new BuildingBox(prop);
- /* parent.buildChildren.push(floor)
- this.meshGroup.add(floor);
- this.entities.push(floor) */
- floor.update()
- this.addEntity(floor,parent)
- //this.selectEntity(floor)
- if(this.selected == parent){//重新选择下,为了显示新楼层线框
- parent.unselect()
- parent.select()
- }
-
- return floor
- },
-
-
-
- startInsertion:function(buildType, parent, sid, name, callback, cancelFun){
-
- let zMin, zMax, entity, resume
- let mapViewport = viewer.mapViewer.viewports[0]
-
- if(buildType == 'resume'){//继续画(使用最后一个点或者新加的点)
- resume = true
- entity = parent
- buildType = parent.buildType
-
- //删除原先所有的点,因为它们已经添加了事件,会很麻烦:
- entity.reDraw(0)
- entity.isNew = true //当作新的来画
- }
-
- if(!resume){
- if(buildType == 'hole' || buildType == 'room'){
- zMin = parent.zMin
- zMax = parent.zMax
- }else if(buildType == 'building'){
- parent = null
- zMin = viewer.bound.boundingBox.min.z
- zMax = viewer.bound.boundingBox.min.z
- }
-
-
-
-
- if(buildType == 'hole'){
- entity = parent.addHole()
- entity.isNew = true
- this.selectEntity(parent)
- entity.select()
- console.log('挖洞 ',entity.uuid)
- }else{
-
- let prop = {
- buildType,
- //name : Potree.config.siteModel.names[buildType],//'building',
- zMin,
- zMax,
- buildParent:parent,
- sid, name,
- ifDraw:true
- }
-
- entity = new BuildingBox(prop);
- entity.isNew = true
- this.selectEntity(entity)
- }
-
-
-
- this.addEntity(entity, parent)
-
-
-
-
- }
-
-
-
-
- let timer;
-
-
- let endDragFun = (e) => {
- if (e.button == THREE.MOUSE.LEFT ) {
- var marker = entity.addMarker({point:entity.points[entity.points.length - 1].clone()})
-
- //entity.editStateChange(true) //重新激活reticule状态
- entity.continueDrag(marker, e)
- } else if (e.button === THREE.MOUSE.RIGHT ) {
- if(e.pressDistance < Potree.config.clickMaxDragDis )end(e);//非拖拽的话
- else entity.continueDrag(null, e/* .drag.object */)
-
- }
- };
- let finish = ()=>{//结束绘画
- viewer.removeEventListener('cancel_insertions', Exit);
- entity.removeEventListener('unselect', Exit);
- clearTimeout(timer)
- entity.editStateChange(false)
- //pressExit && viewer.inputHandler.removeEventListener('keydown', pressExit);
- callback && callback(entity)
- }
- let end = (e={}) => {//尝试结束
- /* 退出的三种形式:
- 1 普通:如果大于三个marker,结束且保留;否则重新画。()
- 2 删除:直接结束且删除。(remove)
- 3 结束:如果大于三个marker,结束且保留;否则结束且删除。 (finish)
- 4 保留:无论几个marker,都保留着,结束。(remain)
- */
-
- if(e.remove){
- finish()
- return this.removeEntity(entity)
- }
-
-
-
- if(!e.remain && !e.finish && !e.remove && entity.markers.length<=minMarkers){//右键 当个数不够时取消
- //重新开始画
- entity.reDraw(1)
-
- viewer.updateVisible(entity.markers[0],'unMove',false);
- var f = ()=>{
- viewer.updateVisible(entity.markers[0],'unMove',true);
- entity.removeEventListener('dragChange',f)
- }
- entity.addEventListener('dragChange',f)
-
- //console.log('waitcontinue')
- entity.continueDrag(entity.markers[0], e)
- return
- }
-
- finish()
-
- if (e.remain || !e.remove && entity.markers.length > 3) {//保留
- entity.removeMarker(entity.points.length - 1);
- entity.markers.forEach(marker=>{marker.dispatchEvent('addHoverEvent') })
- if(buildType == 'room'){
- this.fitPullBox()
- }
- entity.isNew = false
- entity.addMidMarkers()
-
- }else{
- this.removeEntity(entity) //直接删除没画好的,比较简单。这样就不用担心旧的continueDrag仍旧触发了
-
- }
-
- return entity
- };
-
- let Exit = (e)=>{ //强制结束
-
- entity.removeEventListener('unselect', Exit);
-
- if(viewer.inputHandler.drag){//还未触发drop的话
- viewer.inputHandler.drag.object.dispatchEvent({
- type: 'drop',
- drag: viewer.inputHandler.drag,
- viewer: viewer,
- pressDistance:0,
- button : THREE.MOUSE.RIGHT
- });
- viewer.inputHandler.drag = null
- }else{
- end({remain:true})
- }
- viewer.inputHandler.drag = null
- }
-
-
- viewer.dispatchEvent( 'cancel_insertions' );//取消之前的
- viewer.addEventListener('cancel_insertions', Exit);
- entity.addEventListener('unselect', Exit);
-
-
-
- var marker = entity.addMarker({point:new THREE.Vector3(0, 0, 0)})
- viewer.updateVisible(marker,'unMove',false);//这时候的位置是假的(0,0,0)所以先不可见
- var f = ()=>{
- viewer.updateVisible(marker,'unMove',true);
- entity.removeEventListener('dragChange',f)
- }
- entity.addEventListener('dragChange',f)
-
-
- marker.isDragging = true
- viewer.inputHandler.startDragging(marker , {dragViewport:mapViewport, endDragFun, notPressMouse:true} ); //notPressMouse代表不是通过按下鼠标来拖拽. dragViewport指定了只能在地图上拖拽
-
-
- return entity;
-
-
-
-
- },
-
-
-
- getPreDealData(points, zMin, zMax, initial, buildType, parent){
- /* if( buildType == 'building' ){
- zMax = zMin //强制变得一样,作为基底。如果有必要,保存时再算真实的zMax。目前zMin没有保存所以数据是错的,会直接根据floor计算
- } */
-
- var bound = viewer.bound.boundingBox
-
- if(buildType == 'building' && initial){//初始数据错的,要自己建(只有一个building和floor) 原posIsLonlat
- console.log('空间模型未编辑过, 初始化了一个')
-
- points = [
- new THREE.Vector3(bound.min.x, bound.min.y,0),
- new THREE.Vector3(bound.max.x, bound.min.y,0),
- new THREE.Vector3(bound.max.x, bound.max.y,0),
- new THREE.Vector3(bound.min.x, bound.max.y,0),
- ]
- zMin = bound.min.z
- zMax = bound.max.z
- /* points = points.map(e=>{
- return viewer.transform.lonlatToLocal.forward(e)
- }) */
-
- }else{//相对于初始数据集的模型内坐标
- points = points.map(e=> this.transform(e, 'fromDataset'))
- if(buildType == 'floor' && initial){
- zMin = bound.min.z
- zMax = bound.max.z
- }
- }
- return {points, zMax, zMin }
- },
-
-
-
- resetFromData:function(entity, points=[], holes=[], zMin, zMax ){
-
-
- var {points, zMax, zMin} = this.getPreDealData(points, zMin, zMax , this.autoBuild , entity.buildType, entity.buildParent )
-
- if(entity.buildType != 'floor' )entity.points = points
-
-
- if(entity.buildType == 'room'){
- entity.zMin = zMin
- entity.zMax = zMax
- }else if(entity.buildType == 'floor'){//改楼高
- let height = zMax - zMin
- let zMax2 = entity.zMin + height
- SiteModel.changeZ(entity, 'zMax', zMax2)
- }
-
- {
- //删除旧的holes重新添加
- let holesOld = entity.holes
- holesOld.forEach(e=>{
- entity.removeHole(e)
- })
-
- holes.forEach(points =>{
- let ps = points.map(e=> this.transform(e, 'fromDataset'))
- let hole = entity.addHole(ps)
- hole.addMidMarkers()
- })
- }
-
-
-
- entity.update()
- return entity
- }
-
-
- ,
-
- createFromData:function( buildType, parent ,sid, name, points=[], holes=[], zMin, zMax, initial,panos,flagPano){
- if(buildType != 'building' && buildType != 'floor' && buildType != 'room' ) return
-
-
- var {points, zMax, zMin} = this.getPreDealData(points, zMin, zMax , initial, buildType, parent )
-
-
-
- {
-
- let getPano = (id)=>{
- return viewer.images360.panos.find(pano=>pano.id == id)
- }
-
- panos = panos ? panos.map(e=>getPano(e)) : [];
- flagPano = flagPano != void 0 ? getPano(flagPano) : null ; //最中心的pano 或者 最靠近该实体的pano(当panos为空时)
-
- if(!this.editing && buildType == 'floor' && !flagPano){//没有的话可能是自动添加的floor,直接用parent的吧
- panos = parent.panos;
- flagPano = parent.flagPano;
- }
- }
-
-
-
-
-
- let prop = {
- buildType,
- points,
- name,
- sid,
- zMin,
- zMax,
- buildParent:parent,
- ifDraw:this.editing || Potree.settings.drawEntityData,
- panos, flagPano,
- autoBuild : initial
- }
-
- let entity = new BuildingBox(prop)
- SiteModel.addEntity(entity, parent )
- if(this.editing){
- if(buildType == 'building'|| buildType == 'room'){
- entity.addMidMarkers()
- }
- }
-
-
- holes.forEach(points =>{
- let ps = points.map(e=> this.transform(e, 'fromDataset'))
- let hole = entity.addHole(ps)
- this.editing && hole.addMidMarkers()
- })
-
-
- /* if(buildType == 'floor'){
- this.updateBuildingZ(parent)
- } */
-
-
- return entity
- },
-
- transform:function(pos, type){
- if(Potree.settings.editType == 'pano'){ // 模型不经转换
- return new THREE.Vector3().copy(pos).setZ(0);
- }
-
- if(type == 'toDataset'){
- let point = Potree.Utils.datasetPosTransform({ toDataset: true, position: pos.clone(), datasetId: Potree.settings.originDatasetId })
- return new THREE.Vector2().copy(point)
-
- }else{
- let position = new THREE.Vector3().copy(pos).setZ(0)
- return Potree.Utils.datasetPosTransform({ fromDataset: true, position, datasetId: Potree.settings.originDatasetId })
-
- }
- },
-
-
-
-
-
- addEntity:function(entity, parent){
- this.meshGroup.add(entity);
- this.entities.push(entity)
- if(entity.buildType == 'building'){
- this.buildings.push(entity)
- }else{
- parent.buildChildren.push(entity)
-
- }
-
-
- if(entity.buildType == 'room'){
- entity.addEventListener('marker_dropped',()=>{
- this.fitPullBox()
- })
- }else if(entity.buildType == 'floor'){
- this.updateBuildingZ(parent)
- parent.dispatchEvent({type:'addFloor'})
- }
-
- {//仅能存在一个marker被选中。选中的点可以被删除
- entity.addEventListener('clickMarker', (e)=>{
- if(this.selectedMarker == e.marker){
- this.selectedMarker.dispatchEvent({type:'clickSelect',state:false})
- this.selectedMarker = null
- }else{
- if(this.selectedMarker){
- this.selectedMarker.dispatchEvent({type:'clickSelect',state:false})
- }
- this.selectedMarker = e.marker
- this.selectedMarker.dispatchEvent({type:'clickSelect',state:true})
- }
- })
- entity.addEventListener('removeMarker', (e)=>{
- if(this.selectedMarker == e.marker){
- this.selectedMarker = null
- }
- })
-
- let unselect = (e)=>{//取消选中实体或删除后
- if(this.selectedMarker && entity.markers.includes(this.selectedMarker)){
- this.selectedMarker.dispatchEvent({type:'clickSelect',state:false})
- this.selectedMarker = null
- }
- }
- entity.addEventListener('dispose', unselect)
- entity.addEventListener('unselect', unselect)
-
-
- }
- //console.log('添加实体:', entity.buildType, entity.sid, entity.uuid)
-
- },
-
- removeEntity : function(entity){
- if(!this.entities.includes(entity))return
-
- console.log('删除实体:', entity.buildType, entity.sid)
-
- if(this.selected == entity){
- this.height_pull_box.visible = false
- this.selectEntity(null)
- }
-
-
- if(entity.buildType == 'building'){
- var index = this.buildings.indexOf(entity);
- if(index>-1){
- this.buildings.splice(index,1)
- }
- }else{
- var index = entity.buildParent.buildChildren.indexOf(entity);
- if(index>-1){
- entity.buildParent.buildChildren.splice(index,1)
- }
- }
-
-
-
- var index = this.entities.indexOf(entity);
- if(index>-1){
- this.entities.splice(index,1)
- }
-
-
- entity.dispose()
- let buildChildren = entity.buildChildren.slice()
- buildChildren.forEach(e=>this.removeEntity(e))
-
-
- },
-
-
- updateBuildingZ:function(building){
-
- building.buildChildren = building.buildChildren.sort((e,a)=>e.zMin-a.zMin)//从低到高排序
- building.zMin = building.zMax = building.buildChildren[0].zMin //基底高度
- //building.zMax = building.buildChildren[building.buildChildren.length-1].zMax
- if(this.editing) building.update({dontUpdateChildren:true})
- building.dispatchEvent('updateBuildingZ')
- },
-
-
-
- selectEntity : function(entity, state=true){
- if(state === false){
- entity.unselect()
- if(this.selected == entity)this.selected = null
-
- return
- }
-
- if(this.selected == entity || entity && entity.buildType == 'hole')return
- //this.buildings.forEach(e=>e.unselect())
- this.selected && this.selected.unselect()
- this.height_pull_box.visible = false
-
-
- if(entity){
- entity.select()
-
- }
-
-
- this.selected = entity
-
-
- if(entity && (entity.buildType == 'floor' || entity.buildType == 'room' )){
- this.height_pull_box.visible = true
- this.fitPullBox()
- }
-
- if(entity && !entity.isNew && (entity.buildType == 'building' || entity.buildType == 'room' ) && entity.points.length<2){
- this.startInsertion('resume',entity) //继续画
- }
- },
-
-
-
-
-
-
-
- fitPullBox: function(){ //自适应拖拽楼层的pullMesh
- if(!this.selected || this.selected.buildType!= 'floor' && this.selected.buildType!= 'room')return
- let bound = new THREE.Box3();
- bound.expandByObject(this.selected.box)
- let center = bound.getCenter(new THREE.Vector3() )
- let size = bound.getSize(new THREE.Vector3() )
- this.height_pull_box.scale.copy(size)
- this.height_pull_box.position.copy(center)
- },
-
-
-
-
-
- changeZ:function(entity, dirType, value){ // floor or room 修改zMin or zMax
- let max, min //limit
-
- if(entity.buildType == 'floor'){//楼层
- let index = entity.buildParent.buildChildren.indexOf(entity)
- if(dirType == 'zMax'){
- let upper = entity.buildParent.buildChildren[index+1];
- entity.zMax = Math.min(Limit.zMax, value)
- min = entity.zMin + minFloorHeight
- if(entity.zMax < min){
- entity.zMax = min
- }else{
- if(upper){
- max = upper.zMax - minFloorHeight;
- if(entity.zMax > max){
- entity.zMax = max
- }
- }
- }
- if(upper){
- upper.zMin = entity.zMax
- upper.update()
- upper.dispatchEvent({type:'changeHeight'})
- }
- }else{
- let lower = entity.buildParent.buildChildren[index-1];
-
- entity.zMin = Math.max(Limit.zMin, value)
- max = entity.zMax - minFloorHeight
- if(entity.zMin > max){
- entity.zMin = max
- }else{
- if(lower){
- min = lower.zMin + minFloorHeight;
- if(entity.zMin < min){
- entity.zMin = min
- }
- }
- }
- if(lower){
- lower.zMax = entity.zMin
- lower.update()
- lower.dispatchEvent({type:'changeHeight'})
- }
- if(index == 0)this.updateBuildingZ(entity.buildParent)
- }
- }else if(entity.buildType == 'room'){//房间
- //按照navvis的是不一定限制在当前楼层,只要高度不超过当前楼层即可。
- let maxHeight = entity.buildParent.zMax - entity.buildParent.zMin
-
-
- if(dirType == 'zMax'){
- min = entity.zMin + minFloorHeight
- max = entity.zMin + maxHeight
- entity.zMax = THREE.Math.clamp(value, min, max);
- }else{
- min = entity.zMax - maxHeight
- max = entity.zMax - minFloorHeight
- entity.zMin = THREE.Math.clamp(value, min, max);
- }
- }
- entity.update()
- entity.dispatchEvent({type:'changeHeight'})
- //this.selected.emit('update')
- this.fitPullBox()
- },
-
-
-
- createHeightPull:function(){ //拖拽楼层的bounding box
- let boxGeo = new THREE.BoxBufferGeometry( 1, 1, 1/4 )
- let boxMat = new THREE.MeshBasicMaterial({
- color:"#F00",
- opacity:0,
- transparent:true,
- depthTest:false,
- side:2
- })
-
- let height_pull_box_up = new THREE.Mesh(boxGeo,boxMat)
- let height_pull_box_down = new THREE.Mesh(boxGeo,boxMat)
- height_pull_box_up.name = 'height_pull_box_up';
- height_pull_box_down.name = 'height_pull_box_down';
- this.height_pull_box = new THREE.Object3D();
- this.height_pull_box.name = 'height_pull_box'
- this.height_pull_box.add(height_pull_box_up)
- this.height_pull_box.add(height_pull_box_down)
- this.height_pull_box.visible = false
- this.meshGroup.add(this.height_pull_box)
- height_pull_box_up.position.set(0,0,1/2/* 3/8 */)
- height_pull_box_down.position.set(0,0,-1/2/* -3/8 */)
- viewer.setObjectLayers(this.height_pull_box, 'siteModeSideVisi' )
-
-
-
- let mouseover = (e)=>{
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"siteModelFloorDrag"
- })
- }
- let mouseleave = (e)=>{
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"siteModelFloorDrag"
- })
- }
-
-
- let firstZ, firstIntersect;
- let drag = (e)=>{
- var intersectPoint = e.intersectPoint.orthoIntersect //不要点云的intersect,只要orthocamera算出的平面intersect
-
- if(firstIntersect != void 0){
-
- let moveZ = intersectPoint.z - firstIntersect
- if(this.selected.buildType == 'floor'){//楼层
- //限制高度不能超过上下
- if(e.target == height_pull_box_up){
- if(firstZ == void 0)firstZ = this.selected.zMax
- this.changeZ(this.selected, 'zMax', firstZ + moveZ)
- }else{
- if(firstZ == void 0)firstZ = this.selected.zMin
- this.changeZ(this.selected, 'zMin', firstZ + moveZ)
- }
- }else if(this.selected.buildType == 'room'){//房屋
- if(e.target == height_pull_box_up){
- if(firstZ == void 0)firstZ = this.selected.zMax
- this.changeZ(this.selected, 'zMax', firstZ + moveZ)
- }else{
- if(firstZ == void 0)firstZ = this.selected.zMin
- this.changeZ(this.selected, 'zMin', firstZ + moveZ)
- }
- }
- }else{
- firstIntersect = intersectPoint.z
- }
- }
-
-
- let drop = (e)=>{
- firstZ = firstIntersect = null
- }
-
- height_pull_box_up.addEventListener('mousemove',mouseover)
- height_pull_box_down.addEventListener('mousemove',mouseover)
- height_pull_box_up.addEventListener('mouseleave',mouseleave)
- height_pull_box_down.addEventListener('mouseleave',mouseleave)
- height_pull_box_up.addEventListener('drag',drag)
- height_pull_box_down.addEventListener('drag',drag)
- height_pull_box_up.addEventListener('drop',drop)
- height_pull_box_down.addEventListener('drop',drop)
- },
-
-
- pointInWhichEntity(location, buildType, ifIgnoreHole){//buildType是要找的建筑类型
- //location 可以是pano或者坐标
- //由于房间可能在building外,所以房间要另外单独识别。
-
- let lastResult; //最接近的上一层结果,如果没有result返回这个
- let result
- let level = {
- building: 0, floor: 1, room: 2
- }
-
- let traverse = (parent, buildType)=>{//返回第一个符合标准的实体
- let contains;
- if(location instanceof THREE.Vector3){
- contains = parent.ifContainsPoint(location)
- }else{//is pano
- contains = parent.panos.includes(location)
- }
- if(contains){
- if(!lastResult || level[lastResult.buildType] < level[parent.buildType] )lastResult = parent
-
-
- if(parent.buildType == buildType){
- return parent
- }else{
- for(let i=0,len=parent.buildChildren.length; i<len; i++){
- let result1 = traverse(parent.buildChildren[i])
- if(result1) return result1
- }
- }
- }
-
- }
- //因为建筑可能重叠,所以需要先找到最接近其中心的建筑物
- result = Common.sortByScore(this.buildings, [(building)=>{
- return traverse(building, 'building') //在building中
-
- }], [(building)=>{ //写法类似pointInWhichPointcloud
- let boundingBox = building.getBound()
- let center = boundingBox.getCenter(new THREE.Vector3())
- let position = location instanceof THREE.Vector3 ? location : location.position
- let dis = position.distanceTo(center)
- let size = boundingBox.getSize(new THREE.Vector3())
- let length = size.length() / 2;
- return length / dis
- }]);
-
- let building = result && result[0] && result[0].score > 1 && result[0].item
- if(buildType == 'building' || !building)return building
- result = traverse(building, buildType)
-
-
- /* if(!result && buildType == 'room'){//如果要找的是room, 且按刚才的顺序找不到的话,就单独从所有rooms中找一遍。因为room可能不在floor和building内。
- let rooms = this.entities.filter(e=>e.buildType == 'room');
- result = rooms.find(e=>e.ifContainsPoint(position))
- }*/
- //虽然房间可以画到上级之外,但是为了方便起见,假定房间绝对在楼层之内。找不到的话要调整空间模型了。
-
-
- return result || lastResult
-
- }
- ,
-
-
- findPanos: function(){
-
- {
- this.entities.forEach(entity=>{
- //清空:
- entity.panos = []
- entity.flagPano = null
- viewer.images360.panos.forEach(pano=>{
- if(entity.ifContainsPoint(pano.position)){
- entity.panos.push(pano)
- }
- })
- })
- }
-
-
- /* viewer.images360.panos.forEach(pano=>{ //一个漫游点只对应一个实体的话
- let result = this.pointInWhichEntity(pano.position, 'room');
-
- {//get panos for every entities
- let entity = result
- while(entity){
- entity.panos.push(pano);
- entity = entity.buildParent
- }
- }
- }) */
-
- {//search center pano
- this.entities.forEach(entity=>{
- let panos = entity.panos
- if(panos.length == 0)return
- let bound = entity.getBound();
- let center = bound.getCenter(new THREE.Vector3)
- let request = []
- let rank = [
- Images360.scoreFunctions.distanceSquared({position: center})
- ]
- //let panos = entity.panos && entity.panos.length ? entity.panos : viewer.images360.panos //entity没有panos的话,就扩大到所有panos
-
- let r = Common.sortByScore(panos, request, rank);
- if(r && r.length){
- entity.flagPano = r[0].item
- }else{
- console.error('no flagPano??')
- }
-
-
- })
-
- }
-
- }
- ,
-
-
-
- findEntityForDataset:function(){//为每一个数据集寻找它所属的最小实体
- /* var entities = this.entities.filter(e=>e.buildType == 'room' || e.buildType == 'floor' && e.buildChildren.length == 0)
- viewer.scene.pointclouds.forEach(pointcloud=>{
-
- let cloudVolume = pointcloud.getVolume()
- let scores = []
- entities.forEach(entity=>{
- let volume = entity.intersectPointcloudVolume(pointcloud)
- //注:默认已经findPanos过
- let panos = entity.panos.filter(e=>pointcloud.panos.includes(e));
- let panoCount = panos.length
-
- let score = volume / cloudVolume + panoCount / pointcloud.panos.length
-
- scores.push({entity, volume, panoCount, score})
-
- })
- scores.sort((a,b)=>{ return b.score-a.score })
-
- if(scores.length == 0 || scores[0].volume/cloudVolume < 0.0001 && scores[0].volume < 3 ){//如果约等于0
- pointcloud.belongToEntity = null
- }else{
- pointcloud.belongToEntity = scores[0].entity;
- }
- }) */
-
- let getScores = (pointcloud, entities, cloudVolume)=>{
- let scores = []
- entities.forEach(entity=>{
- let volume = entity.intersectPointcloudVolume(pointcloud)
- //注:默认已经findPanos过
- let panos = entity.panos.filter(e=>pointcloud.panos.includes(e));
- let panoCount = panos.length
-
- let score = volume / cloudVolume + panoCount / pointcloud.panos.length
-
- scores.push({entity, volume, panoCount, score})
-
- })
- scores.sort((a,b)=>{ return b.score-a.score })
-
- return scores
- }
-
-
- viewer.scene.pointclouds.forEach(pointcloud=>{ //先判断父级,如果父集不通过就不判断子级。
- let cloudVolume = pointcloud.getVolume()
- let entities = this.buildings
-
- while(1){
- let scores = getScores(pointcloud, entities, cloudVolume)
- if(scores.length == 0 || scores[0].volume/cloudVolume < 0.0001 && scores[0].volume < 3 ){//如果约等于0
- pointcloud.belongToEntity = null
- break;
- }else{
- entities = scores[0].entity.buildChildren
- if(entities.length == 0){
- pointcloud.belongToEntity = scores[0].entity;
- break;
- }
- }
- }
- })
-
-
-
-
-
- /*
- 旧版:
- 只需要考虑 floor 和 room, 因为building的只有一个基底没高度
- floor 和 room 在空间中没有完全的从属关系,因为room可以超出floor之外。所以直接混在一起来查找,但要排除有房间的楼层。
- (现在改为层层递进查找,否则数据集包含entity多的,会直接挂载到体积最大的房间里,即使看起来主体点云并不在该房间)
-
-
- 有的数据集虽然很高,但只有近地面的部分才是主体,这部分一般含有全部漫游点。为了防止上层的实体因体积较大而分数高,就把包含漫游点的个数也加入考虑。
-
- 重叠体积大、且包含漫游点最多的最小实体将会拥有该点云。
-
- 期望: 最好不挂载到最小子级,因为现在有房间都到房间里了。
- */
- }
-
-
-
- ,
- clear:function(){//清空
-
- /* entities:[], //所有实体
- buildings:[], //所有建筑父集
- meshGroup: new THREE.Object3D, */
- this.selectEntity(null)
-
- let length = this.entities.length;
- for(let i=0;i<length;i++){
- this.entities[i].dispose()
- }
-
- this.entities = []
- this.buildings = []
- this.inEntity = null
-
-
- }
- ,
-
-
-
- gotoEntity(id, isNearBy, duration=1000) {
- var entity = this.entities.find(e => e.sid == id)
- let aimPano
- if (!entity) {
- return console.error('没找到entity ')
- }
-
- if(Potree.settings.displayMode == 'showPanos'){
- if (isNearBy && entity.panos.length) {
- if(entity.panos.includes(viewer.images360.currentPano)) return 'posNoChange' //已在当前实体中
- let position = viewer.scene.getActiveCamera().position
- let request = []
- let rank = [Images360.scoreFunctions.distanceSquared({ position })]
- let r = Common.sortByScore(entity.panos, request, rank)
- aimPano = r[0].item
- } else {
- if (!entity.flagPano) {
- return console.log('没有flagPano')
- }
- aimPano = entity.flagPano
- }
- if(aimPano == viewer.images360.currentPano) return 'posNoChange'
- viewer.images360.flyToPano(aimPano)
- }else{
- if(isNearBy && entity.ifContainsPoint(viewer.images360.position) ){
- return 'posNoChange' //已在当前实体中
- }
- let boundingBox = entity.getBound()
- let position = boundingBox.getCenter(new THREE.Vector3()) //中心点不一定在entity中,比如半环形建筑(所以要不要改成到漫游点呢)
-
- if(viewer.modules.Clip && viewer.modules.Clip.editing){
- viewer.modules.Clip.bus.dispatchEvent({type:'flyToPos', position})
- }else{
- if(math.closeTo(position, viewer.images360.position)) return 'posNoChange'
- let size = boundingBox.getSize(new THREE.Vector3())
-
- viewer.scene.view.setView({position, duration})
- viewer.mapViewer.moveTo(position, size, duration)
- }
-
- }
- return true
- },
- focusEntity(id){
- var entity = this.entities.find(e => e.sid == id)
- let boundingBox = entity.getBound()
- let boundSize = boundingBox.getSize(new THREE.Vector3())
- let center = boundingBox.getCenter(new THREE.Vector3())
- this.SplitScreen.focusOnObject(boundSize, center)
-
- this.gotoEntity(id, false, 0)
-
- },
-
- removeIlligalArchi(){//删除marker数量小于3个的建筑,当保存时
- let needDelete = []
-
- this.entities.forEach(e=>{
- if(e.points.length<3){
- needDelete.push(e)
- }
- })
-
- needDelete.forEach(e=>this.removeEntity(e))
-
- },
- }
- /*
- 规则
- 层级:
- type 中文名 改动范围 其他
-
- BUILDING 建筑 xy mesh由自己的基底以及所有floor的组成。如果删除所有floor,就剩一个平面。故而zMin == zMax
- FLOOR 楼层 z(xy未解锁) 点击楼层时房间也会显示,而建筑的其他楼层不显示线框,会显示面. 拖拽高度实际是拖拽楼层间的分界线,楼层之间不会有缝隙
- ROOM 房间 xyz(xy可加锁) 可能超出楼层外,因为楼层拖拽时房间没变。所以建筑不一定包容房间。
- CUSTOM 自定义(现作房间) xyz
- ( xy范绘制时不能超出父级外轮廓,但父级编辑时可以进入子级轮廓。只要能解锁轮廓的都能切洞)
- floor输入高度数值的限制和拖拽是一样的,相当于调节其zMax, 且不能超出其上层的zMax。
- navvis弊端:
- 空间模型不会随着数据集移动而移动 (可以做成跟随,但是如果一个建筑对应多个数据集,那只能跟序号在前的数据集走)
- 建筑点修改后,房间可能飘出建筑外的。 楼层高度修改后也是。
- 调整高度时,看不到相邻的楼层界限,导致拖不动时像bug。
- 调整高度时,侧面看有的重叠的部分比较高亮,感觉是冗余信息?有点乱
-
- 问题:
- 磨砂材质
- 没有阴影,可directionallight 加了呀
-
-
- 暂定一个数据集只属于一个实体(从最小的找起)
-
-
- https://testlaser.4dkankan.com/indoor/t-8KbK1JjubE/api/site_model
- 删点全删光了要删实体吗
- */
- export {SiteModel}
|