Alignment.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. import * as THREE from "../../../../libs/three.js/build/three.module.js";
  2. import SplitScreen4Views from "../../utils/SplitScreen4Views.js"
  3. import math from "../../utils/math.js"
  4. import History from "../../utils/History.js"
  5. var Alignment = {
  6. SplitScreen: SplitScreen4Views,
  7. handleState:null, //操作状态 'translate'|'rotate'
  8. bus: new THREE.EventDispatcher(),
  9. history : new History({
  10. applyData: (data)=>{
  11. data.forEach(item=>{
  12. Alignment.applyTemp(item)
  13. })
  14. },
  15. getData:(pointclouds)=>{
  16. return Alignment.getTemp(pointclouds)
  17. }
  18. }),
  19. /* prepareRecord : true,
  20. writeToHistory(pointclouds){
  21. if(!this.prepareRecord)return;
  22. this.prepareRecord = false
  23. let content = this.getTemp(pointclouds)
  24. this.history.writeIn(content)
  25. },
  26. undo(){//撤销一步
  27. let last = this.history.pop();
  28. last && last.forEach(item=>{
  29. this.applyTemp(item)
  30. })
  31. }, */
  32. applyTemp(item){
  33. var pointcloud = viewer.scene.pointclouds.find(p=>p.dataset_id+p.name == item.sid)
  34. pointcloud.orientationUser = item.orientationUser
  35. pointcloud.translateUser = item.translateUser
  36. this.setMatrix( pointcloud )
  37. },
  38. getTemp(pointclouds){//记录最近一次保存后的状态,便于恢复
  39. pointclouds = pointclouds || viewer.scene.pointclouds
  40. return pointclouds.map(e=>{
  41. return {
  42. sid : e.dataset_id+e.name,
  43. orientationUser : e.orientationUser,
  44. translateUser : e.translateUser.clone(),
  45. }
  46. } )
  47. },
  48. init:function(){
  49. let transfromInfo
  50. viewer.fpControls.addEventListener("transformPointcloud",(e)=>{
  51. if(e.pointclouds[0].dataset_id == Potree.settings.originDatasetId){//禁止手动移动初始数据集
  52. return this.bus.dispatchEvent('forbitMoveOriginDataset')
  53. }
  54. this.history.beforeChange(e.pointclouds)
  55. //this.writeToHistory( e.pointclouds )
  56. if(!transfromInfo){
  57. transfromInfo = {pointclouds:e.pointclouds}
  58. }
  59. if(this.handleState == 'translate'){
  60. e.pointclouds.forEach(cloud=>Alignment.translate(cloud, e.moveVec))
  61. }else if(this.handleState == 'rotate'){
  62. if(Potree.settings.editType == 'pano'){
  63. //旋转中心是intersectStart的版本
  64. /* let center = e.intersectStart //旋转中心是mousedown的位置
  65. if(e.intersect.equals(center))return
  66. if(!transfromInfo){
  67. transfromInfo = {
  68. orientationUser : e.pointclouds[0].orientationUser,
  69. //vecStart : e.moveVec, // 首次移动向量 只有八个方向,精度太小,所以延迟
  70. pointclouds: e.pointclouds
  71. }
  72. this.bus.dispatchEvent({type:'rotateStart', startPoint:center})
  73. return
  74. }else if(!transfromInfo.vecStart){
  75. let vec = new THREE.Vector3().subVectors(e.intersect, center).setZ(0)
  76. if(vec.length() * e.camera.zoom > 30){ //在屏幕上距离初始点有一定距离后开始算
  77. //console.log('moveVec',vec)
  78. transfromInfo.vecStart = vec
  79. } */
  80. let center = e.pointclouds[0].translateUser //旋转中心是第一个点云的位置
  81. if(e.intersect.equals(center))return
  82. if(!transfromInfo.vecStart){
  83. transfromInfo.orientationUser = e.pointclouds[0].orientationUser
  84. transfromInfo.vecStart = new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0)
  85. }else{
  86. let vec = new THREE.Vector3().subVectors(e.intersect, center).setZ(0)
  87. let angle = math.getAngle(transfromInfo.vecStart,vec,'z')
  88. let diffAngle = transfromInfo.orientationUser + angle - transfromInfo.pointclouds[0].orientationUser
  89. transfromInfo.pointclouds.forEach(cloud=>{
  90. /* let centerNoTranfrom = Potree.Utils.datasetPosTransform({ toDataset: true, pointcloud:cloud, position: center }) //中心点在数据集中的位置
  91. Alignment.rotate(cloud, null, diffAngle)
  92. let centerNow = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:cloud, position: centerNoTranfrom }) //中心点的现在位置
  93. let shift = new THREE.Vector3().subVectors( center, centerNow); //偏移量
  94. Alignment.translate(cloud,shift) //使center还保留在原位
  95. //let centerNow1 = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:transfromInfo.pointcloud, position: centerNoTranfrom }) //中心点的现在位置
  96. */
  97. Alignment.rotateAround(center, cloud, null, diffAngle)
  98. })
  99. }
  100. //this.bus.dispatchEvent({type:'rotate', endPoint: e.intersect})
  101. }else{
  102. let center = e.pointclouds[0].translateUser //移动到的位置就是中心
  103. if(e.intersect.equals(center))return
  104. if(!transfromInfo.vecStart){
  105. transfromInfo.orientationUser = e.pointclouds[0].orientationUser
  106. transfromInfo.vecStart = new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0)
  107. }else{
  108. let vec = new THREE.Vector3().subVectors(e.intersect, center).setZ(0)
  109. let angle = math.getAngle(transfromInfo.vecStart,vec,'z')
  110. let diffAngle = transfromInfo.orientationUser + angle - transfromInfo.pointclouds[0].orientationUser
  111. Alignment.rotate(transfromInfo.pointclouds[0], null, diffAngle)
  112. }
  113. }
  114. }
  115. })
  116. viewer.fpControls.addEventListener("end",(e)=>{
  117. transfromInfo && this.history.afterChange(transfromInfo.pointclouds )
  118. transfromInfo = null
  119. })
  120. viewer.inputHandler.addEventListener('keydown',e=>{
  121. if(e.keyCode == 90 && e.event.ctrlKey){//Z
  122. this.history.undo()
  123. }else if(e.keyCode == 89 && e.event.ctrlKey){//Y
  124. this.history.redo()
  125. }
  126. })
  127. // cursor:
  128. let updateCursor = (e)=>{
  129. if(e.drag || !this.editing)return //仅在鼠标不按下时更新:
  130. let handleState = Alignment.handleState
  131. if(e.hoverViewport.alignment && handleState && e.hoverViewport.alignment[handleState]){
  132. if(handleState == 'translate'){
  133. if( e.intersect && e.intersect.location ){
  134. viewer.dispatchEvent({
  135. type : "CursorChange", action : "add", name:"movePointcloud"
  136. })
  137. }else{
  138. viewer.dispatchEvent({
  139. type : "CursorChange", action : "remove", name:"movePointcloud"
  140. })
  141. }
  142. }else if(handleState == 'rotate'){
  143. if( e.intersect && e.intersect.location ){
  144. viewer.dispatchEvent({
  145. type : "CursorChange", action : "add", name:"rotatePointcloud"
  146. })
  147. }else{
  148. viewer.dispatchEvent({
  149. type : "CursorChange", action : "remove", name:"rotatePointcloud"
  150. })
  151. }
  152. }
  153. }else{
  154. //清空:
  155. viewer.dispatchEvent({
  156. type : "CursorChange", action : "remove", name:"movePointcloud"
  157. })
  158. viewer.dispatchEvent({
  159. type : "CursorChange", action : "remove", name:"rotatePointcloud"
  160. })
  161. }
  162. }
  163. viewer.addEventListener('global_mousemove',updateCursor)
  164. viewer.addEventListener('global_drop',updateCursor)//拖拽结束
  165. viewer.addEventListener('updateModelBound', (e)=>{
  166. if(this.editing){
  167. this.SplitScreen.updateCameraOutOfModel()
  168. }
  169. })
  170. },
  171. setMatrix : function(pointcloud){
  172. var vec1 = pointcloud.position //position为数据集内部的偏移,在navvis中对应的是dataset.pointCloudSceneNode的children[0].position
  173. var vec2 = pointcloud.translateUser
  174. var angle = pointcloud.orientationUser
  175. var pos1Matrix = new THREE.Matrix4().setPosition(vec1);//先移动到点云本身应该在的初始位置(在4dkk里和其他应用中都是在这个位置的,也能和漫游点对应上)
  176. var rotMatrix = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0,0,1), angle)//再旋转
  177. var pos2Matrix = new THREE.Matrix4().setPosition(vec2);//最后是平移
  178. var matrix = new THREE.Matrix4().multiplyMatrices(pos2Matrix, rotMatrix);
  179. pointcloud.transformMatrix = matrix.clone();//为该数据集的变化矩阵。 对应navvis的m2w_
  180. pointcloud.transformInvMatrix.copy(matrix).invert()
  181. pointcloud.rotateMatrix = rotMatrix
  182. pointcloud.rotateInvMatrix.copy(rotMatrix).invert()
  183. pointcloud.panos.forEach(e=>e.transformByPointcloud())
  184. matrix = new THREE.Matrix4().multiplyMatrices(matrix, pos1Matrix);
  185. pointcloud.matrix = matrix;
  186. //pointcloud.matrixWorldNeedsUpdate = true //更新matrixWorld (非计算,直接赋值)
  187. pointcloud.updateMatrixWorld(true)
  188. if(this.editing){
  189. Alignment.changeCallBack && Alignment.changeCallBack();
  190. }
  191. if(pointcloud.spriteNodeRoot){
  192. pointcloud.spriteNodeRoot.matrixWorld.copy(pointcloud.matrixWorld)//.multiplyMatrices(pointcloud.matrixWorld, pointcloud.matrixWorld);
  193. }
  194. viewer.boundNeedUpdate = true
  195. //pointcloud.updateBound()
  196. pointcloud.getPanosBound()
  197. viewer.dispatchEvent('content_changed')
  198. },
  199. rotateAround(center, pointcloud, deg, angle){//绕center点转动
  200. var angle = angle != void 0 ? angle : THREE.Math.degToRad(deg)
  201. let vec1 = new THREE.Vector3().subVectors(pointcloud.translateUser, center);
  202. let rotMatrix = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0,0,1), angle)
  203. let vec2 = vec1.clone().applyMatrix4(rotMatrix) //将到旋转中心的偏差也转动
  204. let vec3 = new THREE.Vector3().subVectors(vec2,vec1); //这个就是多出来的一步translateUser
  205. this.rotate(pointcloud, deg, angle)
  206. this.translate(pointcloud, vec3)
  207. //绕点转动就是比普通转动多一步移动到相对center的某个位置。 1 初始点云移动到自己的position; 2 移动一个vec1 3绕原点旋转 4再移动一个原本的translateUser。 绘制出来后发现移动量就是第二步vec旋转后的偏移
  208. },
  209. rotate:function(pointcloud, deg, angle){//绕各自中心转动(各自的position) 假设点云位移position后0,0,0就是它的中心了(根据navvis观察这样做是绕同一个点旋转的)
  210. var angle = angle != void 0 ? angle : THREE.Math.degToRad(deg) //正逆负顺
  211. pointcloud.orientationUser += angle
  212. Alignment.setMatrix(pointcloud)
  213. },
  214. translate:function(pointcloud, vec){
  215. pointcloud.translateUser.add(vec)
  216. Alignment.setMatrix(pointcloud)
  217. },
  218. enter:function(){
  219. //this.saveTemp()
  220. this.originData = this.getTemp()
  221. this.SplitScreen.split({alignment:true})
  222. viewer.images360.panos.forEach(pano=>{
  223. Potree.Utils.updateVisible(pano.mapMarker, 'split4Screens', false)
  224. })
  225. viewer.viewports.find(e=>e.name == 'mapViewport').alignment = {rotate:true,translate:true};
  226. viewer.viewports.find(e=>e.name == 'right').alignment = {translate:true, translateVec:new THREE.Vector3(0,0,1)}; //只能上下移动
  227. viewer.viewports.find(e=>e.name == 'back').alignment = {translate:true, translateVec:new THREE.Vector3(0,0,1)}; //只能上下移动
  228. this.editing = true
  229. viewer.updateFpVisiDatasets()
  230. },
  231. leave:function(){
  232. this.switchHandle(null)
  233. /* this.originData.forEach(e=>{//恢复
  234. var pointcloud = viewer.scene.pointclouds.find(p=>p.dataset_id == e.id)
  235. this.translate(pointcloud, new THREE.Vector3().subVectors(e.translateUser , pointcloud.translateUser))
  236. this.rotate(pointcloud, null, e.orientationUser - pointcloud.orientationUser)
  237. }) */
  238. this.originData.forEach(e=>{//恢复
  239. this.applyTemp(e)
  240. })
  241. this.SplitScreen.recover()
  242. viewer.images360.panos.forEach(pano=>{
  243. Potree.Utils.updateVisible(pano.mapMarker, 'split4Screens', true)
  244. })
  245. this.editing = false
  246. this.history.clear()
  247. viewer.updateFpVisiDatasets()
  248. viewer.dispatchEvent({
  249. type : "CursorChange", action : "remove", name:"movePointcloud"
  250. })
  251. viewer.dispatchEvent({
  252. type : "CursorChange", action : "remove", name:"rotatePointcloud"
  253. })
  254. }
  255. ,
  256. switchHandle:function(state){
  257. this.handleState = state
  258. //清空:
  259. viewer.dispatchEvent({
  260. type : "CursorChange", action : "remove", name:"movePointcloud"
  261. })
  262. viewer.dispatchEvent({
  263. type : "CursorChange", action : "remove", name:"rotatePointcloud"
  264. })
  265. this.bus.dispatchEvent({type:'switchHandle' , state })
  266. },
  267. save: function(){//保存所有数据集的位置和旋转
  268. let callback = ()=>{//保存成功后
  269. this.originData = this.getTemp() //this.saveTemp();
  270. //需要修改 测量线的position。漫游点已经实时修改了
  271. viewer.scene.measurements.forEach(e=>e.transformByPointcloud())
  272. viewer.images360.updateCube(viewer.bound)
  273. }
  274. var data = viewer.scene.pointclouds.map(e=>{
  275. let pos = viewer.transform.lonlatToLocal.inverse(e.translateUser.clone())
  276. return {
  277. id: e.dataset_id,
  278. orientation : e.orientationUser,
  279. location:[pos.x, pos.y, pos.z],
  280. //transformMatrix: e.transformMatrix.elements,
  281. }
  282. })
  283. //data = JSON.stringify(data)
  284. //test: 退出后保留结果
  285. if(!Potree.settings.isOfficial){
  286. callback()
  287. }
  288. return {data, callback}
  289. }
  290. }
  291. /*
  292. 关于控制点:
  293. 两个控制点只能打在同一个数据集上。传输这两个点的4dkk中的本地坐标和经纬度,后台算出该数据集的旋转平移,
  294. 然后其他数据集绕该数据集旋转,并保持相对位置不变。
  295. */
  296. export {Alignment}