ctrlPolygon.js 22 KB

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