Clipping.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. import * as THREE from "../../../../libs/three.js/build/three.module.js";
  2. import math from "../../utils/math.js"
  3. import Common from '../../utils/Common.js'
  4. import {LineDraw, MeshDraw} from "../../utils/DrawUtil.js";
  5. import {ExtendView} from "../../../viewer/ExtendView.js";
  6. import Viewport from "../../viewer/Viewport.js";
  7. import Sprite from "../../objects/Sprite.js";
  8. import {transitions, easing, lerp} from '../../utils/transitions.js'
  9. import {TransformControls} from "../../objects/tool/TransformControls.js";
  10. import SplitScreen from "../../utils/SplitScreen.js"
  11. //import History from "../../utils/History.js"
  12. const cameraProps = [
  13. {
  14. name : 'top',
  15. axis:["x","y"],
  16. direction : new THREE.Vector3(0,0,-1), //镜头朝向
  17. openCount:0,
  18. }
  19. ]
  20. export class Clipping extends THREE.EventDispatcher{ //实时剪裁
  21. constructor(){
  22. super()
  23. this.views = {}
  24. this.cameras = {}
  25. this.orthoCamera = new THREE.OrthographicCamera(-100, 100, 100, 100, 0.01, 10000)
  26. this.orthoCamera.up.set(0,0,1)
  27. }
  28. init(){
  29. if(this.inited)return
  30. this.initViews()
  31. this.inited = true
  32. this.prepareRecord = true
  33. this.activeViewName = 'mainView'
  34. this.events = {
  35. transfromCallback:(e)=>{//拖拽变化时
  36. this.adjustCamHeight()
  37. //检测漫游点、回退等
  38. /* if(this.prepareRecord){
  39. let box = viewer.transformationTool.selection[0]
  40. this.history.writeIn({box, matrix:box.matrix.clone()})
  41. this.prepareRecord = false
  42. } */
  43. },
  44. /* onTransfromEnd:(e)=>{//拖拽结束、松开
  45. this.prepareRecord = true
  46. }, */
  47. selectCallback:(e)=>{
  48. this.adjustCamHeight()
  49. let unableNavigate = this.activeViewName != 'mainView' || e.selection.length > 0
  50. if(Potree.settings.unableNavigate && !unableNavigate){
  51. setTimeout(()=>{
  52. Potree.settings.unableNavigate = this.activeViewName != 'mainView' || e.selection.length > 0
  53. },300)//延迟是因为点击时取消选择后可能立即就会触发flyToPano。 而且有的人喜欢点两下
  54. }else Potree.settings.unableNavigate = unableNavigate
  55. },
  56. onkeydown:(e)=>{
  57. if(e.keyCode == 8 || e.keyCode == 46){// Backspace or Delete
  58. viewer.inputHandler.selection[0] && viewer.scene.removeVolume(viewer.inputHandler.selection[0]);
  59. }
  60. }
  61. }
  62. /* this.history = new History({ //也可以写到全局,但需要加个判断物品是否存在的函数
  63. applyData: (data)=>{
  64. if(viewer.scene.volumes.includes(data.box)){
  65. data.matrix.decompose( data.box.position, data.box.quaternion, data.box.scale );
  66. }else{
  67. this.history.undo()//找不到就回退下一个。(直接写这?)
  68. }
  69. }
  70. }) */
  71. }
  72. initViews(){
  73. this.splitScreenTool = new SplitScreen
  74. for(let i=0;i<1;i++){
  75. let prop = cameraProps[i];
  76. let view = new ExtendView()
  77. this.views[prop.name] = view
  78. this.cameras[prop.name] = this.orthoCamera
  79. view.direction = prop.direction
  80. }
  81. this.views.mainView = viewer.mainViewport.view
  82. this.cameras.mainView = viewer.mainViewport.camera
  83. }
  84. switchView(name){//替换view和camera到mainViewport
  85. if(this.activeViewName == name)return
  86. let view = this.views[name]
  87. let camera = this.cameras[name]
  88. let prop = cameraProps.find(e=>e.name == name)
  89. let {boundSize, center, boundingBox} = viewer.bound
  90. this.lastViewName = this.activeViewName
  91. this.activeViewName = name
  92. let lastView = this.views[this.lastViewName]
  93. let lastCamera = this.cameras[this.lastViewName]
  94. viewer.mainViewport.view = view
  95. viewer.mainViewport.camera = camera
  96. if(lastCamera)lastView.zoom = lastCamera.zoom
  97. /* if(lastView){//2d->3d
  98. view.copy(lastView)
  99. } */
  100. if(name == 'mainView'){
  101. Potree.settings.unableNavigate = false
  102. /* viewer.transformationTool.handles['scale.z+'].node.visible = true
  103. viewer.transformationTool.handles['scale.z-'].node.visible = true */
  104. }else{
  105. Potree.settings.unableNavigate = true
  106. /* viewer.transformationTool.handles['scale.z+'].node.visible = false
  107. viewer.transformationTool.handles['scale.z-'].node.visible = false */
  108. if(prop.openCount == 0){//至多执行一次
  109. //this.viewportFitBound(name, boundSize, center)
  110. this.orthoMoveFit(center, {bound:boundingBox}, 0)
  111. this.camHeightOutOfModel = view.position.z //记录下此刻相机高度。
  112. }
  113. prop.openCount++
  114. this.adjustCamHeight()
  115. /* this.targetPlane.setFromNormalAndCoplanarPoint( view.direction.clone(), center )
  116. this.targetPlane.projectPoint(view.position, this.shiftTarget ) //target转换到过模型中心的平面,以保证镜头一定在模型外
  117. view.position.copy(this.splitScreenTool.getPosOutOfModel(viewer.mainViewport)) */
  118. if(view.zoom)camera.zoom = view.zoom//恢复上次的zoom
  119. }
  120. viewer.updateScreenSize({forceUpdateSize:true})//更新camera aspect left等
  121. if(viewer.inputHandler.selection.length){
  122. this.focusOnObject(viewer.inputHandler.selection[0])
  123. }
  124. }
  125. focusOnObject(box, duration=0){
  126. if(this.activeViewName == 'mainView'){
  127. viewer.focusOnObject({boundingBox:box.boundingBox.clone().applyMatrix4(box.matrixWorld)}, 'boundingBox', duration)
  128. }else{
  129. this.orthoMoveFit(box.position, {bound:box.boundingBox.clone().applyMatrix4(box.matrixWorld)}, duration)
  130. }
  131. this.adjustCamHeight()
  132. }
  133. orthoMoveFit(pos, info, duration){
  134. var margin = {x:viewer.mainViewport.resolution.x*0.4, y:viewer.mainViewport.resolution.y*0.4}
  135. this.splitScreenTool.viewportFitBound(viewer.mainViewport, info.bound, pos, duration, margin )
  136. }
  137. adjustCamHeight(){
  138. if(this.activeViewName != 'top')return
  139. let view = this.views.top
  140. let height
  141. if(viewer.inputHandler.selection.length){ //相机高度位于选中的box的顶部
  142. let box = viewer.inputHandler.selection[0]
  143. height = box.boundingBox.clone().applyMatrix4(box.matrixWorld).max.z;
  144. }else{
  145. height = this.camHeightOutOfModel //显示全部点云
  146. }
  147. view.position.z = height
  148. //console.log('adjustCamHeight',height)
  149. //缺点:1 会导致缩放很小的时候,transformationTool的轴因放大到了相机背面。(只有scale轴做了处理)
  150. //2 无法直接切换 看不到的box,但可以先取消选择
  151. //3 但是俯视图中无法切换到被上层盖住的box(不过把俯视图作为辅助,只针对单个box调动的话,问题不大)
  152. }
  153. enter(){
  154. this.init()
  155. viewer.transformationTool.setModeEnable(['translation'])
  156. //viewer.transformationTool.handles['rotation.x'].node.visible = false
  157. viewer.transformationTool.frame.material.visible = false //不盖住boxVolume的frame
  158. this.targetPlane = viewer.mainViewport.targetPlane = new THREE.Plane()
  159. this.shiftTarget = viewer.mainViewport.shiftTarget = new THREE.Vector3 //project在targetPlane上的位置
  160. this.getAllBoxes().forEach(box=>{
  161. Potree.Utils.updateVisible(box,'hidden',true) //显现
  162. })
  163. viewer.transformationTool.history.clear()
  164. viewer.transformationTool.addEventListener('transformed', this.events.transfromCallback)
  165. //viewer.transformationTool.addEventListener('stopDrag', this.events.onTransfromEnd)
  166. viewer.inputHandler.addEventListener('selection_changed', this.events.selectCallback)
  167. viewer.inputHandler.addEventListener('keydown', this.events.onkeydown)
  168. this.setPointLevelAuto()
  169. var initialPointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == Potree.settings.originDatasetId)
  170. //隐藏 初始数据集以外的数据集
  171. viewer.scene.pointclouds.forEach(e=>{
  172. if(e.dataset_id!=Potree.settings.originDatasetId){
  173. Potree.Utils.updateVisible(e,'enterClipping',false)
  174. //Potree.settings.floorplanEnables[e.dataset_id] = false
  175. e.panos.forEach(pano=>pano.setEnable(false)) //禁止漫游
  176. }else{
  177. Potree.Utils.updateVisible(e,'enterClipping',true, 1, 'add')
  178. //Potree.settings.floorplanEnables[e.dataset_id] = true
  179. }
  180. })
  181. viewer.flyToDataset({ pointcloud : initialPointcloud, duration:0})
  182. }
  183. leave(){
  184. viewer.transformationTool.setModeEnable(['scale', 'translation', 'rotation'] )
  185. viewer.transformationTool.frame.material.visible = true //恢复
  186. this.switchView( 'mainView' )
  187. this.getAllBoxes().forEach(box=>{
  188. Potree.Utils.updateVisible(box,'hidden',false)//隐身
  189. })
  190. viewer.transformationTool.removeEventListener('transformed', this.events.transfromCallback)
  191. //viewer.transformationTool.removeEventListener('stopDrag', this.events.onTransfromEnd)
  192. viewer.inputHandler.removeEventListener('selection_changed', this.events.selectCallback)
  193. //viewer.inputHandler.removeEventListener('keydown', this.events.onkeydown)
  194. viewer.transformObject(null)
  195. viewer.transformationTool.history.clear()
  196. //恢复 初始数据集以外的数据集
  197. viewer.scene.pointclouds.forEach(e=>{
  198. if(e.dataset_id!=Potree.settings.originDatasetId){
  199. Potree.Utils.updateVisible(e,'enterClipping',true)
  200. e.panos.forEach(pano=>pano.setEnable(true))
  201. }else{
  202. Potree.Utils.updateVisible(e,'enterClipping',false, 0, 'cancel')
  203. }
  204. })
  205. }
  206. setTranMode(mode){//rotate or translate
  207. this.tranMode = mode
  208. viewer.transformationTool.setModeEnable([mode])
  209. }
  210. //问:是否要显示其他数据集
  211. setPointLevelAuto(){
  212. /*
  213. let visiCount = viewer.images360.panos.length
  214. let maxCount = 200, minCount = 20, minPer = 0.7, maxPer = 1
  215. let percent = maxPer - ( maxPer - minPer) * THREE.Math.clamp((visiCount - minCount) / (maxCount - minCount),0,1)
  216. Potree.settings.UserDensityPercent = percent ---还是不限制了,尤其是平面图希望更细致点,毕竟剪裁主要要看清剪裁的部位。
  217. */
  218. viewer.setPointBudget(5*1000*1000); //给个中等到高等之间的质量
  219. Potree.settings.sizeFitToLevel = true
  220. viewer.setPointLevels()
  221. }
  222. getAllBoxes(){
  223. return viewer.scene.volumes.filter(v=>v.clip && v instanceof Potree.BoxVolume )
  224. }
  225. getCalcData(){//给后台矩阵数据,以裁剪点云。
  226. let Clip = viewer.modules.Clip //裁剪下载模块
  227. let data = {
  228. transformation_matrix: viewer.scene.pointclouds.filter(p=>p.dataset_id == Potree.settings.originDatasetId).map((cloud)=>{
  229. let data = {
  230. id: cloud.dataset_id,
  231. matrix : new THREE.Matrix4().elements, //参照downloadNoCrop,给默认值,表示没有最外层裁剪
  232. VisiMatrixes: cloud.material.clipBoxes_in.filter(e=>!e.box.isNew).map(e=>Clip.getTransformationMatrix(cloud, e.inverse).elements),
  233. UnVisiMatrixes: cloud.material.clipBoxes_out.filter(e=>!e.box.isNew).map(e=>Clip.getTransformationMatrix(cloud, e.inverse).elements),
  234. modelMatrix:(new THREE.Matrix4).copy(cloud.transformMatrix).transpose().elements
  235. }
  236. return data
  237. }) ,
  238. aabb: "b-12742000 -12742000 -12742000 12742000 12742000 12742000" //剪裁空间
  239. }
  240. return data
  241. }
  242. saveClipData(){//输出所有的clip volumeBox
  243. let oldState = !viewer.clipUnabled;
  244. viewer.setClipState(true)
  245. let data = this.getAllBoxes().filter(e=>!e.isNew).map(volume=>{
  246. return {
  247. clipTask: volume.clipTask,
  248. position: Potree.Utils.datasetPosTransform({position:volume.position, toDataset: true, datasetId: Potree.settings.originDatasetId}).toArray(),
  249. rotation: Potree.Utils.datasetRotTransform({rotation:volume.rotation, toDataset: true, datasetId: Potree.settings.originDatasetId, getRotation:true}).toArray().slice(0,3),
  250. scale: volume.scale.toArray(),
  251. }
  252. })
  253. console.log(data)
  254. console.log(JSON.stringify(data))
  255. viewer.setClipState(oldState)
  256. return data
  257. }
  258. loadFromData(data=[]){
  259. data.forEach(v=>{
  260. let volume = new Potree.BoxVolume({clip:true, clipTask:v.clipTask});
  261. volume.scale.fromArray(v.scale);
  262. volume.position.fromArray(v.position);
  263. volume.rotation.fromArray(v.rotation);
  264. volume.position.copy(Potree.Utils.datasetPosTransform({position:volume.position, fromDataset: true, datasetId:Potree.settings.originDatasetId}))
  265. volume.rotation.copy(Potree.Utils.datasetRotTransform({rotation:volume.rotation, fromDataset: true, datasetId:Potree.settings.originDatasetId, getRotation:true}))
  266. viewer.scene.addVolume(volume);
  267. viewer.volumeTool.scene.add(volume);
  268. })
  269. }
  270. }
  271. //注意:实时裁剪只对初始数据集有效