ctrlPolygon.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import {LineDraw, MeshDraw} from "../utils/DrawUtil";
  3. import math from "./math.js";
  4. const verticalLine = new THREE.Line3()
  5. //控制点和边的合集。具有可以拖拽修改的功能,拖拽时能防止线相交。
  6. export class ctrlPolygon extends THREE.Object3D {
  7. constructor (type, prop) {
  8. super()
  9. this.type = type
  10. this.maxMarkers = Number.MAX_SAFE_INTEGER;
  11. this.transformData(prop);
  12. for(let i in prop){
  13. this[i] = prop[i]
  14. }
  15. if(this.closed && this.dimension == '2d'){
  16. this.areaPlane = this.createAreaPlane();
  17. this.add(this.areaPlane)
  18. }
  19. //数据--刚开始一定是空的
  20. this.points = [];
  21. //mesh 不一定有
  22. this.markers = [];
  23. this.edges = [];
  24. this.center
  25. }
  26. initData(prop){
  27. //开始加数据
  28. if(prop.points){
  29. for(const p of prop.points){
  30. const pos = new THREE.Vector3().copy(p)
  31. this.addMarker({point:pos});
  32. }
  33. if(this.datasetId != void 0){//初始化位置
  34. if(this.dataset_points){
  35. this.dataset_points = this.dataset_points.map(e=>{
  36. return e && new THREE.Vector3().copy(e)
  37. })
  38. this.transformByPointcloud() //根据dataset_points生成points
  39. }
  40. }else{
  41. if(prop.dataset_points && prop.dataset_points.some(e=>e != void 0)){
  42. console.error('存在测量线的datasetId为空而dataset_points有值,请检查并删除:'+this.sid)//存在过的bug,原因未知,可能是后台处理dataset时替换的错误:http://192.168.0.21/index.php?m=bug&f=view&bugID=23601
  43. console.log(this)
  44. }
  45. }
  46. this.getFacePlane()
  47. this.getPoint2dInfo(this.points)
  48. this.update(true)
  49. //this.dragChange(new THREE.Vector3().copy(prop.points[prop.points.length-1]), prop.points.length-1);
  50. this.setSelected(false )
  51. this.addHoverEvent()
  52. }
  53. }
  54. addHoverEvent(){ //当非isNew时才添加事件
  55. this.markers.forEach(marker =>{
  56. let mouseover = (e) => {
  57. this.setMarkerSelected(e.object, true, 'single');/* console.log('hover') */
  58. viewer.dispatchEvent({
  59. type : "CursorChange", action : "add", name:"markerMove"
  60. })
  61. };
  62. let mouseleave = (e) => {
  63. this.setMarkerSelected(e.object, false, 'single');/* console.log('hoveroff') */
  64. viewer.dispatchEvent({
  65. type : "CursorChange", action : "remove", name:"markerMove"
  66. })
  67. }
  68. marker.addEventListener('mouseover', mouseover);
  69. marker.addEventListener('mouseleave', mouseleave);
  70. })
  71. }
  72. addMarker(o={}){
  73. var index = o.index == void 0 ? this.points.length : o.index //要当第几个
  74. this.points = [...this.points.slice(0,index), o.point, ...this.points.slice(index,this.points.length)]
  75. //this.points.push(o.point);
  76. if(o.marker){
  77. this.add(o.marker)
  78. this.markers = [...this.markers.slice(0,index), o.marker, ...this.markers.slice(index,this.markers.length)]
  79. this.updateMarker(o.marker, o.point)
  80. o.marker.addEventListener('drag', this.dragMarker.bind(this));
  81. o.marker.addEventListener('drop', this.dropMarker.bind(this));
  82. }
  83. if(o.edge){
  84. this.add(o.edge)
  85. this.edges = [...this.edges.slice(0,index), o.edge, ...this.edges.slice(index,this.edges.length)]
  86. }
  87. }
  88. dragMarker(e){
  89. var I, atMap
  90. if(e.hoverViewport != e.drag.dragViewport)return //不能使用e.dragViewport,要使用drag中的,因为drag中存储的要一直继承下来,不因mouseup了而改变。
  91. atMap = e.drag.dragViewport.name == 'mapViewport'
  92. if(atMap && this.unableDragAtMap){
  93. e.drag.object = null //取消拖拽
  94. return
  95. }
  96. I = e.intersectPoint && (e.intersectPoint.orthoIntersect || e.intersectPoint.location)
  97. //记录数据集
  98. //在三维中脱离点云(在map中拉到周围都没有点云的地方)的顶点,无法拖拽怎么办
  99. if (I) {
  100. atMap && (I.z = 0)
  101. let i = this.markers.indexOf(e.drag.object);
  102. if (i !== -1) {
  103. this.dragChange(I, i, atMap)
  104. if(this.points_datasets){
  105. if(e.intersectPoint.pointcloud) this.points_datasets[i] = e.intersectPoint.pointcloud.dataset_id
  106. else this.points_datasets[i] = null
  107. }
  108. }
  109. this.editStateChange(true)
  110. return true
  111. }
  112. };
  113. dragChange(intersectPos, i, atMap){
  114. var len = this.markers.length
  115. var location = intersectPos.clone()
  116. if(this.faceDirection && this.maxMarkers == 2 && len == 2){//add 固定方向的点不直接拖拽
  117. var p1 = this.markers[0].position
  118. if(this.faceDirection == 'horizontal'){
  119. var projectPos = location.clone().setZ(p1.z)
  120. }else{
  121. var projectPos = p1.clone().setZ(location.z)
  122. }
  123. //var p2 = p1.clone().add(this.direction)
  124. //var projectPos = math.getFootPoint(location, p1, p2)
  125. LineDraw.updateLine(this.guideLine, [location, projectPos])
  126. location = projectPos
  127. this.guideLine.visible = true
  128. }else if( len > 1){
  129. var points = this.points.map(e=>e.clone())
  130. points[i].copy(location) //算normal需要提前确认point
  131. //若为定义了面朝向的矩形
  132. if(this.faceDirection == 'horizontal'){
  133. if(len == 2){
  134. location.setZ(points[0].z)
  135. }
  136. if(!this.facePlane){//一个点就能确定面
  137. this.facePlane = new THREE.Plane().setFromNormalAndCoplanarPoint( new THREE.Vector3(0,0,1), this.points[0] )
  138. }
  139. }else if(this.faceDirection == 'vertical'){//当有两个点时, 有两个方向的可能
  140. if(len == 2){
  141. if(this.isRect){
  142. let vec = points[0].clone().sub(location)
  143. if(Math.sqrt(vec.x*vec.x+vec.y*vec.y) > Math.abs(vec.z) ){//水平(高度差小于水平距离时)
  144. location.setZ(points[0].z)
  145. //this.cannotConfirmNormal = false;//能确定面为水平方向
  146. }else{//垂直 (当两点一样时也属于这种)
  147. location.setX(points[0].x);
  148. location.setY(points[0].y);
  149. //this.cannotConfirmNormal = true; //不能确定面,因第三点可绕着纵轴线自由移动
  150. }
  151. }
  152. }else{
  153. {//判断cannotConfirmNormal. 如果前几段都在竖直线上,就不能固定出面方向。
  154. this.cannotConfirmNormal = true
  155. let max = this.isRect ? 1 : len-2
  156. for(let i=0;i<max;i++){
  157. let p1 = points[i].clone()
  158. let p2 = points[i+1].clone()
  159. let vec = p1.sub(p2);
  160. if(vec.x != 0 || vec.y != 0){
  161. this.cannotConfirmNormal = false
  162. break;
  163. }
  164. }
  165. }
  166. if(!this.facePlane || this.cannotConfirmNormal){//三个点且为水平方向时,计算面
  167. var points_ = points.map(e=>new THREE.Vector2(e.x,e.y))
  168. var points2 = getDifferentPoint(points_, 2);
  169. if(points2){
  170. let normal = math.getNormal2d({p1:points2[0], p2:points2[1]})
  171. normal = new THREE.Vector3(normal.x, normal.y, 0)
  172. this.facePlane = new THREE.Plane().setFromNormalAndCoplanarPoint( normal, this.points[0] )
  173. }
  174. }
  175. }
  176. }
  177. if(len > 2){//area
  178. if(!this.faceDirection){
  179. if(len == 3 || this.isRect) this.cannotConfirmNormal = true //当第三个点固定后(有四个点时)才能固定面
  180. if(!this.facePlane || this.cannotConfirmNormal){
  181. var points3 = getDifferentPoint(points, 3);//只有找到三个不同的点算拥有面和area
  182. if(points3){
  183. this.facePlane = new THREE.Plane().setFromCoplanarPoints(...points3 )
  184. }
  185. }
  186. }
  187. if( this.facePlane && !this.cannotConfirmNormal ){//之后加的点一定要在面上
  188. if(atMap){
  189. //地图上用垂直线,得到和面的交点。
  190. verticalLine.set(location.clone().setZ(100000), location.clone().setZ(-100000))//确保长度范围覆盖所有测量面
  191. location = this.facePlane.intersectLine(verticalLine, new THREE.Vector3() )
  192. if(!location) return;
  193. }else{
  194. location = this.facePlane.projectPoint(intersectPos, new THREE.Vector3() )
  195. }
  196. }
  197. points[i].copy(location)//再copy确认一次
  198. if(this.isRect){ //是矩形 (即使没有faceDirection也能执行)
  199. //根据前两个点计算当前和下一个点
  200. var p1 = points[(i-2+len)%len]
  201. var p2 = points[(i-1+len)%len]
  202. if(p1.equals(p2)){//意外情况:重复点两次 ( bug点,改了好多遍)
  203. if(this.faceDirection == 'vertical'){
  204. p2.add(new THREE.Vector3(0,0,0.0001))
  205. }else{
  206. p2.add(new THREE.Vector3(0,0.0001,0))
  207. }
  208. }
  209. //p3 : location
  210. var foot = math.getFootPoint(location, p1, p2)//p2 修改p2到垂足的位置
  211. var vec = foot.clone().sub(location)
  212. var p4 = p1.clone().sub(vec)
  213. points[(i-1+len)%len].copy(foot)
  214. points[(i+1)%len].copy(p4)
  215. this.setPosition((i-1+len)%len, foot);//p2
  216. this.setPosition((i+1)%len, p4);
  217. }
  218. /* let points2d;
  219. if(this.facePlane){
  220. var originPoint0 = points[0].clone()
  221. var qua = math.getQuaBetween2Vector(this.facePlane.normal, new THREE.Vector3(0,0,1), new THREE.Vector3(0,0,1));
  222. points2d = points.map(e=>e.clone().applyQuaternion(qua))
  223. } */
  224. this.getPoint2dInfo(points)
  225. var isIntersectSelf = !this.isRect && len > 3 && this.intersectSelf(this.point2dInfo.points2d)//检测相交
  226. if(isIntersectSelf){
  227. //not-allowed
  228. viewer.dispatchEvent({
  229. type : "CursorChange", action : "add", name:"polygon_isIntersectSelf"
  230. })
  231. this.isIntersectSelf = true
  232. return
  233. }else{
  234. this.isIntersectSelf = false
  235. viewer.dispatchEvent({
  236. type : "CursorChange", action : "remove", name:"polygon_isIntersectSelf"
  237. })
  238. /* this.facePlane && (this.point2dInfo = {
  239. originPoint0 ,
  240. points2d,
  241. quaInverse : qua.clone().invert()
  242. }) */
  243. }
  244. }
  245. var showGuideLine = len>1 && (this.faceDirection || len > 3)
  246. if(showGuideLine && this.guideLine){
  247. LineDraw.updateLine(this.guideLine, [intersectPos, location])
  248. this.guideLine.visible = true
  249. }
  250. //console.log(this.points.map(e=>e.toArray()))
  251. }
  252. if(this.retrictArea && !math.isPointInArea(this.retrictArea.points, location)){
  253. viewer.dispatchEvent({
  254. type : "CursorChange", action : "add", name:"polygon_AtWrongPlace"
  255. })
  256. this.isAtWrongPlace = true
  257. return
  258. }else{
  259. viewer.dispatchEvent({
  260. type : "CursorChange", action : "remove", name:"polygon_AtWrongPlace"
  261. })
  262. this.isAtWrongPlace = false
  263. this.setPosition(i, location);
  264. this.update()
  265. this.dispatchEvent({type:'dragChange', index:i})
  266. }
  267. }
  268. dropMarker(e){
  269. if (this.isNew && e.pressDistance>Potree.config.clickMaxDragDis){//拖拽的话返回
  270. return continueDrag(e, this)
  271. }
  272. if(e.isTouch){
  273. if(e.hoverViewport != viewer.mainViewport && this.unableDragAtMap){
  274. viewer.emit('reticule_forbit', true)
  275. console.log('reticule_forbit',true)
  276. return continueDrag(e, this)
  277. }else{
  278. viewer.emit('reticule_forbit', false)
  279. console.log('reticule_forbit',false)
  280. }
  281. this.dragMarker(e) //触屏时必须先更新下点
  282. }
  283. if (e.button != THREE.MOUSE.RIGHT && (//右键click的话继续执行,因为会停止
  284. this.isIntersectSelf && this.isNew //有线相交了
  285. || this.isAtWrongPlace && this.isNew
  286. || !e.isAtDomElement && this.isNew//如果是刚添加时在其他dom点击, 不要响应
  287. || e.hoverViewport != viewer.mainViewport && this.unableDragAtMap //垂直的测量线不允许在地图上放点
  288. || !getDifferentPoint(this.points, this.points.length) //不允许和之前的点相同
  289. )
  290. ){
  291. return continueDrag(e, this)
  292. }
  293. //console.log('drop marker' )
  294. let i = this.markers.indexOf(e.drag.object);
  295. if (i !== -1) {
  296. this.dispatchEvent({
  297. 'type': 'marker_dropped',
  298. 'index': i
  299. });
  300. if(this.markers.length>2 && this.facePlane)this.cannotConfirmNormal = false
  301. this.guideLine &&(this.guideLine.visible = false)
  302. }
  303. this.setMarkerSelected(e.drag.object, false, 'single')
  304. this.editStateChange(false)
  305. e.drag.endDragFun && e.drag.endDragFun(e)// addmarker
  306. if(this.changeCallBack)this.changeCallBack()
  307. return true
  308. };
  309. getFacePlane(){//最普通一种get方法,根据顶点。且假设所有点已经共面,且不重合
  310. if(this.points.length<3) return
  311. this.facePlane = new THREE.Plane().setFromCoplanarPoints(...this.points.slice(0,3) )
  312. }
  313. getPoint2dInfo(points){ //在更新areaplane之前必须更新过point2dInfo
  314. if(this.facePlane){
  315. var originPoint0 = points[0].clone()
  316. var qua = math.getQuaBetween2Vector(this.facePlane.normal, new THREE.Vector3(0,0,1), new THREE.Vector3(0,0,1));
  317. let points2d = points.map(e=>e.clone().applyQuaternion(qua))
  318. this.point2dInfo = {
  319. originPoint0 ,
  320. points2d,
  321. quaInverse : qua.clone().invert()
  322. }
  323. }
  324. }
  325. setPosition (index, position) {//拖拽后设置位置
  326. let point = this.points[index];
  327. point.copy(position);
  328. if(this.datasetId){
  329. this.dataset_points[index] = Potree.Utils.datasetPosTransform.toDataset({datasetId:this.datasetId, position:point.clone()})
  330. }
  331. let marker = this.markers[index];
  332. this.updateMarker(marker, point)
  333. }
  334. updateMarker(marker, pos){
  335. marker.position.copy(pos);
  336. marker.update();
  337. }
  338. intersectSelf(points2d){//add
  339. var len = points2d.length
  340. for(var i=0;i<len;i++){
  341. for(var j=i+2;j<len;j++){
  342. if(Math.abs(j-len-i)<2)continue;//不和邻边比
  343. var p1 = points2d[i]
  344. var p2 = points2d[i+1]
  345. var p3 = points2d[j]
  346. var p4 = points2d[(j+1)%len]
  347. if(p1.equals(p2) || p3.equals(p4) || p1.equals(p3) || p2.equals(p3) || p1.equals(p4) || p2.equals(p4))continue
  348. var line1 = [p1,p2]
  349. var line2 = [p3,p4]
  350. var intersect = math.isLineIntersect(line1, line2, false, 0.001)
  351. if(intersect){
  352. return true
  353. break
  354. }
  355. }
  356. }
  357. }
  358. removeMarker (index) {
  359. this.points.splice(index, 1);
  360. this.remove(this.markers[index]);
  361. this.markers.splice(index, 1);
  362. let edgeIndex = index//(index === 0) ? 0 : (index - 1);
  363. this.remove(this.edges[edgeIndex]);
  364. this.edges.splice(edgeIndex, 1);
  365. this.point2dInfo && this.point2dInfo.points2d.splice(index, 1); //add
  366. //this.update();
  367. }
  368. createAreaPlane(mat){
  369. var geometry = new THREE.Geometry();
  370. var mesh = new THREE.Mesh(geometry, mat)
  371. return mesh
  372. }
  373. updateAreaPlane(){
  374. if(!this.point2dInfo)return
  375. this.areaPlane.geometry.dispose()
  376. if(this.points.length>2){
  377. this.areaPlane.geometry = MeshDraw.getShapeGeo(this.point2dInfo.points2d)
  378. var center = math.getCenterOfGravityPoint(this.point2dInfo.points2d) //重心
  379. var firstPos = this.point2dInfo.points2d[0].clone()
  380. firstPos.z = 0 //因为shape只读取了xy,所以位移下, 再算出最终位置,得到差距
  381. firstPos.applyQuaternion(this.point2dInfo.quaInverse)
  382. var vec = this.point2dInfo.originPoint0.clone().sub(firstPos)
  383. center = new THREE.Vector3(center.x, center.y, 0)
  384. center.applyQuaternion(this.point2dInfo.quaInverse)
  385. this.areaPlane.quaternion.copy(this.point2dInfo.quaInverse)
  386. this.areaPlane.position.copy(vec)
  387. center.add(vec)
  388. this.center = center
  389. }else{
  390. this.areaPlane.geometry = new THREE.Geometry()
  391. }
  392. }
  393. update(ifUpdateMarkers){
  394. if(this.points.length === 0){
  395. return;
  396. }
  397. let lastIndex = this.points.length - 1;
  398. for (let index = 0; index <= lastIndex; index++) {
  399. let nextIndex = (index + 1 > lastIndex) ? 0 : index + 1;
  400. let previousIndex = (index === 0) ? lastIndex : index - 1;
  401. let point = this.points[index];
  402. let nextPoint = this.points[nextIndex];
  403. let previousPoint = this.points[previousIndex];
  404. if(ifUpdateMarkers){
  405. this.updateMarker(this.markers[index], point)
  406. }
  407. { // edges
  408. let edge = this.edges[index];
  409. if(edge){
  410. LineDraw.updateLine(edge, [point, nextPoint])
  411. //edge.visible = index < lastIndex || this.isRect || (this.closed && !this.isNew);
  412. }
  413. }
  414. }
  415. if(this.areaPlane){
  416. this.updateAreaPlane()
  417. }
  418. viewer.mapViewer.emit('content_changed')
  419. }
  420. dispose(){//add
  421. this.parent.remove(this)
  422. }
  423. reDraw(restMarkerCount=0){//重新开始画
  424. let pointCount = this.points.length - restMarkerCount // restMarkerCount为需要留下的marker数量
  425. while(pointCount > 0){
  426. this.removeMarker(--pointCount)
  427. }
  428. this.point2dInfo = null
  429. this.facePlane = null
  430. }
  431. setMarkerSelected(){}
  432. editStateChange(state){
  433. if(!state){
  434. viewer.dispatchEvent({
  435. type : "CursorChange", action : "remove", name:"polygon_isIntersectSelf"
  436. })
  437. viewer.dispatchEvent({
  438. type : "CursorChange", action : "remove", name:"polygon_AtWrongPlace"
  439. })
  440. viewer.emit('reticule_forbit', false)
  441. }
  442. }
  443. transformData(){}
  444. setSelected(){}
  445. }
  446. function getDifferentPoint(points, count){//for facePlane
  447. var result = [];
  448. for(let i=0;i<points.length;i++){
  449. var p = points[i];
  450. if(result.find(e=>e.equals(p)))continue;
  451. else result.push(p)
  452. if(result.length == count)break
  453. }
  454. if(result.length == count)return result
  455. }
  456. function continueDrag(e, this_){
  457. setTimeout(()=>{//等 drag=null之后 //右键拖拽结束后需要重新得到drag
  458. if(this_.parent){//没被删的话
  459. viewer.inputHandler.startDragging(e.drag.object,
  460. {endDragFun: e.drag.endDragFun, notPressMouse:e.drag.notPressMouse, dragViewport:e.drag.dragViewport}
  461. )
  462. }
  463. },1)
  464. }