Measure.js 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333
  1. import * as THREE from "../../../../libs/three.js/build/three.module.js";
  2. import {TextSprite} from "../TextSprite.js";
  3. import {Utils} from "../../../utils.js";
  4. import Label from "../Label.js";
  5. import {LineDraw} from "../../utils/DrawUtil.js";
  6. import math from "../../utils/math.js";
  7. import DepthBasicMaterial from "../../materials/DepthBasicMaterial.js";
  8. import Sprite from '../Sprite.js'
  9. import {config} from '../../settings.js'
  10. import browser from "../../utils/browser.js";
  11. import {ctrlPolygon} from './ctrlPolygon.js'
  12. let texLoader = new THREE.TextureLoader()
  13. let defaultColor = new THREE.Color(config.measure.default.color);
  14. let highlightColor = new THREE.Color(config.measure.highlight.color);
  15. let color = new THREE.Color(config.measure.color)
  16. let textColor = new THREE.Color(config.measure.textColor)
  17. var markerMats;
  18. var lineMats;
  19. var planeMats
  20. const textSizeRatio = math.linearClamp(window.innerWidth * window.innerHeight , 360*720, 1920*1080, 0.75, 1) //pc字显示大一些
  21. const lineDepthInfo = {
  22. clipDistance : 4,//消失距离
  23. occlusionDistance: 1,//变为backColor距离
  24. }
  25. const markerMapShrink = browser.isMobile() ? 0.4 : 0.8 //触屏需要更大的热区
  26. const markerSizeInfo = {
  27. width2d : 18 / markerMapShrink , nearBound : 1.5, farBound : 15,
  28. }
  29. /* const markerSizeInfo = {
  30. minSize : 10 , maxSize : 15 , nearBound : 1.5, farBound : 15,
  31. } */
  32. const labelSizeInfo = {width2d:200}
  33. /* const mainLabelProp = {
  34. backgroundColor: {r: defaultColor.r*255, g: defaultColor.g*255, b: defaultColor.b*255, a:config.measure.default.opacity},
  35. textColor: {r: textColor.r*255, g: textColor.g*255, b: textColor.b*255, a: 1.0},
  36. fontsize:16,
  37. useDepth : true ,
  38. renderOrder : 5, pickOrder:5,
  39. clipDistance : 20,//消失距离
  40. occlusionDistance: 5,//变为backColor距离
  41. maxOcclusionFactor:0.7,
  42. } */
  43. const mainLabelProp = {
  44. //backgroundColor: {r: defaultColor.r*255, g: defaultColor.g*255, b: defaultColor.b*255, a:config.measure.default.opacity},
  45. backgroundColor: {r: 0, g: 0, b: 0, a:0},
  46. textColor: {r: textColor.r*255, g: textColor.g*255, b: textColor.b*255, a: 1.0},
  47. textBorderColor: {r:255, g: 255, b:255, a: 1.0},
  48. textBorderThick:3 ,
  49. fontsize: 15 * textSizeRatio,
  50. borderRadius : 12, margin:{x:20,y:4},
  51. renderOrder : 5, pickOrder:5,
  52. disToLine:-0.15,
  53. useDepth : true ,
  54. // 2023.10 尽量不让数字被挡住
  55. clipDistance : 10,//消失距离
  56. occlusionDistance: 10,//变为backColor距离
  57. maxOcclusionFactor:0.3,
  58. maxClipFactor:0.8
  59. }
  60. const subLabelProp = {
  61. backgroundColor: {r: 255, g: 255, b: 255, a:0},
  62. textColor: {r: textColor.r*255, g: textColor.g*255, b: textColor.b*255, a: 1.0},
  63. textBorderColor: {r:255, g: 255, b:255, a: 1.0},
  64. textBorderThick:3 ,
  65. fontsize: 15 * textSizeRatio,
  66. renderOrder : 4, pickOrder:4,
  67. disToLine:-0.13,
  68. }
  69. /* const subLabelProp = {
  70. backgroundColor: {r: 255, g: 255, b: 255, a:1},
  71. textColor: {r: 0, g: 0, b:0, a: 1.0},
  72. fontsize:14,
  73. renderOrder : 4, pickOrder:4,
  74. }
  75. */
  76. const angle = THREE.Math.degToRad(5);//显示水平垂直辅助线的最小角度
  77. const guideShowMinAngle = {min: angle, max: Math.PI/2 - angle}
  78. export class Measure extends ctrlPolygon{
  79. constructor (prop) {
  80. prop.dimension = '2d'
  81. super('measure',prop);
  82. this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
  83. this.name = this.measureType + this.constructor.counter //'Measure_' + this.constructor.counter;
  84. this.markerLabels = [];
  85. this.edgeLabels = [];
  86. this.angleLabels = [];
  87. this.coordinateLabels = [];
  88. this.area = {value:0,string:''}
  89. if( this.showArea ){
  90. this.areaLabel = this.createAreaLabel();
  91. this.add(this.areaLabel)
  92. }
  93. //add:
  94. if(this.atPlane || this.faceDirection){ //是一个平面上的话
  95. this.createGuideLine();
  96. }
  97. if(this.measureType == 'Distance' /* || this.measureType.includes('MulDistance') */){
  98. this.createHorVerGuideLine()
  99. }
  100. this.selectStates = {}
  101. this.setUnitSystem(prop.unit || viewer.unitConvert.UnitService.defaultSystem)
  102. Potree.Utils.setObjectLayers(this, 'measure' )
  103. //addMarkers:
  104. this.initData(prop)
  105. this.points_datasets || (this.points_datasets = []) //存每个点是哪个数据集
  106. this.addEventListener('marker_dropped',(e)=>{
  107. this.updateDatasetBelong(e.index)
  108. })
  109. this.addEventListener('isVisible', ()=>{
  110. viewer.mapViewer && viewer.mapViewer.dispatchEvent({type:'content_changed'})
  111. })
  112. }
  113. initData(prop){
  114. let makeIt = super.initData(prop)
  115. if(makeIt){
  116. this.edges.forEach(edge=>{edge.dispatchEvent('addHoverEvent') })
  117. }else{
  118. this.failBuilded = true
  119. }
  120. }
  121. updateDatasetBelong(changeIndex){//更新所属数据集
  122. if(Potree.settings.editType == "merge"){//无地图
  123. /* this.dataset_points = this.points.map((e,i)=>{
  124. return Potree.Utils.datasetPosTransform({toDataset:true, datasetId:this.points_datasets[i], position:e.clone()})
  125. }) */
  126. this.dataset_points[changeIndex] = Potree.Utils.datasetPosTransform({toDataset:true, datasetId:this.points_datasets[changeIndex], position:this.points[changeIndex].clone()})
  127. return
  128. }
  129. let old = this.datasetId
  130. let maxCount = {id:null,count:0}
  131. let datasets = {}
  132. this.points_datasets.forEach(e=>{
  133. if(e == void 0)return
  134. if(datasets[e]){
  135. datasets[e] ++
  136. }else{
  137. datasets[e] = 1
  138. }
  139. })
  140. for(let i in datasets) {
  141. if(datasets[i]>maxCount.count){
  142. maxCount = {id:i, count:datasets[i]}
  143. }
  144. }
  145. this.datasetId = maxCount.count > 0 ? maxCount.id : null
  146. //if(this.datasetId != old){
  147. //this.dispatchEvent({type:'changeDatasetId'})
  148. if(this.datasetId == void 0){
  149. this.dataset_points = null //可能为空或[null,null...]
  150. }else{
  151. this.dataset_points = this.points.map(e=>{
  152. return Potree.Utils.datasetPosTransform({toDataset:true,datasetId:this.datasetId, position:e.clone()})
  153. })
  154. }
  155. //}
  156. }
  157. transformByPointcloud(){//每次移动点云 or 加载测量线时要获取一下当前position //有地图时
  158. if(this.datasetId == void 0)return
  159. this.points = this.dataset_points.map(e=>{
  160. return Potree.Utils.datasetPosTransform({fromDataset:true, datasetId:this.datasetId, position:e.clone()})
  161. })
  162. this.getPoint2dInfo(this.points)
  163. this.update({ifUpdateMarkers:true})
  164. this.setSelected(false)//隐藏edgelabel
  165. }
  166. update(options={}) {
  167. super.update(options)
  168. if(this.showCoordinates && this.points.length>0){
  169. let position = this.points[0];
  170. this.markers[0].position.copy(position);
  171. { // coordinate labels
  172. let coordinateLabel = this.coordinateLabels[0];
  173. let lonlat = viewer.transform.lonlatToLocal.inverse(position.toArray())
  174. let EPSG4550 = viewer.transform.lonlatTo4550.forward(lonlat)
  175. let pos = [
  176. position.toArray(),
  177. lonlat,
  178. EPSG4550
  179. ]
  180. //let msg = position.toArray().map(p => Utils.addCommas(p.toFixed(2))).join(" / ");
  181. let msg = pos.map(a=>
  182. a.map(p => Utils.addCommas(p.toFixed(10))).join(", ")
  183. ).join("<br>")
  184. coordinateLabel.setText(msg);
  185. coordinateLabel.setPos(position)
  186. coordinateLabel.setVisible(true)//this.showCoordinates;
  187. }
  188. return
  189. }
  190. let setEdgeLabel = (label,p1,p2,distance)=>{//设置label位置和字
  191. this.setEdgeLabelPos(label,p1,p2)
  192. distance = distance == void 0 ? p1.distanceTo(p2) : distance;
  193. //var text = viewer.unitConvert.convert(distance, 'distance', Potree.settings.precision, this.unitSystem, 1 , true)//distance要传0.1 这个factor
  194. var text = viewer.unitConvert.convert(distance, 'distance', Potree.settings.precision , this.unitSystem, 0.01 , true )//distance要传0.1 这个factor
  195. label.setText(text)
  196. return distance
  197. }
  198. /* let setEdgeLabel = (label,p1,p2,distance)=>{//设置label位置和字
  199. this.setEdgeLabelPos(label,p1,p2)
  200. distance = distance == void 0 ? p1.distanceTo(p2) : distance;
  201. var text = this.labelText || viewer.unitConvert.convert(distance, 'distance', Potree.settings.precision , this.unitSystem, 0.001 , true, true)//distance要传0.1 这个factor
  202. label.setText(text)
  203. } */
  204. let lastIndex = this.points.length - 1;
  205. for (let index = 0; index <= lastIndex; index++) {
  206. let nextIndex = (index + 1 > lastIndex) ? 0 : index + 1;
  207. let previousIndex = (index === 0) ? lastIndex : index - 1;
  208. //if(!this.closed && nextIndex == 0 )break; //add
  209. let point = this.points[index];
  210. let nextPoint = this.points[nextIndex];
  211. let previousPoint = this.points[previousIndex];
  212. if(this.showDistances){ // edge labels
  213. let edgeLabel = this.edgeLabels[index];
  214. let distance = point.distanceTo(nextPoint)
  215. edgeLabel.shouldVisi = (index < lastIndex || this.isRect || this.closed && !this.isNew ) && distance>0
  216. /* this.closed || */edgeLabel.setVisible(edgeLabel.shouldVisi)
  217. if(edgeLabel.visible){
  218. edgeLabel.lineDir = new THREE.Vector3().subVectors(point,nextPoint).normalize() //[point,nextPoint]
  219. setEdgeLabel(edgeLabel,point,nextPoint,distance)
  220. }
  221. }
  222. }
  223. if(this.measureType == 'Distance' && this.points.length>1){//设置水平垂直辅助线
  224. var pTop, pBtm
  225. if(this.points[0].z > this.points[1].z ){
  226. pTop = this.points[0];
  227. pBtm = this.points[1];
  228. }else{
  229. pTop = this.points[1];
  230. pBtm = this.points[0];
  231. }
  232. let projectPos = new THREE.Vector3(pTop.x, pTop.y, pBtm.z);//两条guideline的交点
  233. {//倾斜角度太小的时候不显示
  234. let tan = pTop.distanceTo(projectPos) / pBtm.distanceTo(projectPos)
  235. let angle = Math.atan(tan);
  236. this.shouldShowHorVerGuide = angle > guideShowMinAngle.min && angle < guideShowMinAngle.max
  237. }
  238. LineDraw.updateLine(this.verGuideEdge, [pTop, projectPos])
  239. LineDraw.updateLine(this.horGuideEdge, [pBtm, projectPos])
  240. setEdgeLabel(this.verEdgeLabel,pTop,projectPos)
  241. setEdgeLabel(this.horEdgeLabel,pBtm,projectPos)
  242. this.verGuideEdge.visible = this.horGuideEdge.visible = this.shouldShowHorVerGuide
  243. this.verEdgeLabel.visible = this.horEdgeLabel.visible = this.shouldShowHorVerGuide
  244. }
  245. if(this.showArea && this.point2dInfo){ // update area
  246. /* if(this.points.length>2){
  247. this.area = {value:0};
  248. this.areaLabel.setVisible(false)
  249. }else{ */
  250. let area = Math.abs(math.getArea(this.point2dInfo.points2d))//this.getArea();
  251. let msg = viewer.unitConvert.convert(area, 'area', Potree.settings.precision, this.unitSystem/* , 0.1 */ )
  252. this.area = {value:area, string:msg}
  253. this.areaLabel.setPos(this.center);
  254. this.areaLabel.setText(msg);
  255. this.areaLabel.setVisible(true)
  256. //}
  257. }
  258. };
  259. setEdgeLabelPos(label,p1,p2){ //调整label的位置,使倾斜后看起来在线的中心,而不要挡住端点
  260. let center = new THREE.Vector3().addVectors(p1,p2).multiplyScalar(0.5);
  261. return label.setPos(center)
  262. if(label.lineDir && label.lineDir.length() > 0){
  263. if(viewer.mainViewport.camera.type == 'OrthographicCamera'){
  264. label.setPos(center)
  265. }else{
  266. //根据视线和线的夹角(后又加入相机和两个端点距离差)来决定标签偏移位置。+
  267. let eyePos = viewer.mainViewport.camera.position;
  268. let dir = viewer.mainViewport.view.direction //new THREE.Vector3().subVectors(center,eyePos).normalize()
  269. /*let centerDir = new THREE.Vector3().subVectors(center,eyePos).normalize()
  270. if(centerDir.dot(dir)<0){//中点在相机后方,就不设置
  271. label.setPos(center)
  272. return
  273. } */
  274. let cos = dir.dot(label.lineDir)
  275. let nearPoint = cos > 0 ? p2 : p1 //近端点。
  276. let far = cos > 0 ? p1 : p2 //远端点。
  277. let nearPointDir = new THREE.Vector3().subVectors(nearPoint,eyePos)//.normalize()
  278. //使label在中点和近端点中变化, 近端点可能到了相机后方,需要投影到相机所在平面上
  279. if(nearPointDir.dot(dir)<0){//近端点到了相机后方,前移。
  280. //let hfov = cameraLight.getHFOVForCamera(viewer.mainViewport.camera , true ); //暂且只看水平fov
  281. //if(nearPointDir.dot(dir)<Math.cos(hfov/2)){//近端点在镜头外,前移。 --但是这个就得把点转化成在镜头边缘而非左右两边(camDirPlane上)
  282. let ray = new THREE.Raycaster()
  283. ray.set(nearPoint, cos>0?label.lineDir:label.lineDir.clone().negate())
  284. let camDirPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(dir, eyePos)
  285. nearPoint = ray.ray.intersectPlane(camDirPlane, new THREE.Vector3())
  286. if(!nearPoint){//线是垂直的,视线是水平的时候
  287. return label.setPos(center)
  288. }
  289. }
  290. //防止离远了之后也偏移很多,但远了之后相机到端点vec和到中点的vec的夹角接近,不需要怎么偏移的。
  291. let dis1 = nearPoint.distanceToSquared(eyePos)
  292. let dis2 = far.distanceToSquared(eyePos)
  293. let diff = Math.abs(dis1/dis2)
  294. diff<1 && (diff = 1/diff)
  295. diff = math.linearClamp(diff,0, 30, 0,1 )
  296. let efficiency = 0.7; // 0-1 数值越高,r越容易接近1或-1,label越容易在倾斜后靠近近端点。
  297. //let r = 0.5*efficiency*cos + 0.5
  298. let r = 0.5*efficiency*diff*cos + 0.5
  299. r = THREE.Math.clamp(r,0.1,0.9)
  300. //视线越接近线的方向,标签应该越往近端点偏移,防止看起来几乎在远端。
  301. if(cos > 0){
  302. center = p1.clone().multiplyScalar(1-r).add(nearPoint.clone().multiplyScalar(r)); //label在线上滑动,使尽量保持在视觉中心
  303. }else{
  304. center = nearPoint.clone().multiplyScalar(1-r).add(p2.clone().multiplyScalar(r)); //label在线上滑动,使尽量保持在视觉中心
  305. }
  306. label.setPos(center)
  307. }
  308. //归零
  309. //this.orient2dInfo = null
  310. //this.markers.forEach(e=>e.needsUpdate=true)
  311. }else{
  312. label.setPos(center)
  313. }
  314. }
  315. addMarker (o={}) {
  316. let marker = new Sprite({mat:this.getMarkerMaterial('default'), sizeInfo: markerSizeInfo, name:"measure_point"} )
  317. Potree.Utils.setObjectLayers(marker, 'measure' )
  318. marker.pickOrder = marker.renderOrder = 3
  319. marker.markerSelectStates = {}
  320. marker.addEventListener('startDragging',(e)=>{
  321. if(e.drag.dragViewport.name == 'MainView')viewer.inputHandler.dispatchEvent( {type: 'isMeasuring',v:true, cause:'startDragging'})
  322. })
  323. marker.addEventListener('drop',(e)=>{
  324. viewer.inputHandler.dispatchEvent({type: 'isMeasuring', v:false, cause:'stopDragging'} )
  325. })
  326. //marker.measure = this
  327. let edge
  328. { // edges
  329. edge = LineDraw.createFatLine( [ ],{mat:this.getLineMat('edgeDefault')} )
  330. edge.pickOrder = 0
  331. Potree.Utils.setObjectLayers(edge, 'measure' )
  332. let addHoverEvent = ()=>{ //当非isNew时才添加事件
  333. let mouseover = (e) => {this.setSelected(true, 'edge')};
  334. let mouseleave = (e) => {this.setSelected(false, 'edge')};
  335. edge.addEventListener('mouseover', mouseover);
  336. edge.addEventListener('mouseleave', mouseleave);
  337. edge.removeEventListener('addHoverEvent', addHoverEvent);
  338. }
  339. edge.addEventListener('addHoverEvent', addHoverEvent);
  340. }
  341. super.addMarker({point:o.point, marker:marker, edge})
  342. if(this.showEdges){ // edge labels
  343. const edgeLabel = this.createEdgeLabel('edgeLabel', !this.closed)
  344. this.edgeLabels.push(edgeLabel);
  345. }
  346. if(this.showCoordinates){ // coordinate labels
  347. let coordinateLabel = new Label({
  348. className:'measure_pointPos',
  349. camera: viewer.scene.getActiveCamera()
  350. })
  351. coordinateLabel.setVisible(false)
  352. this.coordinateLabels.push(coordinateLabel);
  353. }
  354. let event = {
  355. type: 'marker_added',
  356. measurement: this,
  357. marker: marker
  358. };
  359. this.dispatchEvent(event);
  360. //this.setMarker(this.points.length - 1, point);
  361. this.update()//更新一下倒数第二条线
  362. return marker;//add
  363. };
  364. editStateChange(state){ //主要针对edgeLabels显示切换,编辑时显示
  365. super.editStateChange(state)
  366. if(!state){
  367. this.editStateTimer = setTimeout(()=>{
  368. if(!this.isEditing){
  369. this.dispatchEvent({type:'editStateChange',state:false})
  370. this.setEdgesDisplay(false)
  371. this.areaPlane && Potree.Utils.updateVisible(this.areaPlane, 'intersectLastLine', true)
  372. this.areaLabel && Potree.Utils.updateVisible(this.areaLabel, 'intersectLastLine', true)
  373. }
  374. },100)
  375. }else{
  376. if(!this.isEditing){
  377. this.dispatchEvent({type:'editStateChange',state:true})
  378. this.setEdgesDisplay(true)
  379. clearTimeout(this.editStateTimer)
  380. }
  381. }
  382. this.isEditing = state
  383. }
  384. setMarkerSelected(marker, state, hoverObject){
  385. //console.warn(marker.id , state, hoverObject)
  386. marker.markerSelectStates[hoverObject] = state
  387. let absoluteState = false
  388. for(var i in marker.markerSelectStates){
  389. if(marker.markerSelectStates[i] == 'hover'){
  390. absoluteState = true; break;
  391. }
  392. }
  393. if(absoluteState){
  394. marker.material = this.getMarkerMaterial('select')
  395. }else{
  396. marker.material = this.getMarkerMaterial('default')
  397. }
  398. marker.selected = absoluteState
  399. viewer.mapViewer && viewer.mapViewer.dispatchEvent('content_changed')
  400. viewer.dispatchEvent('content_changed')
  401. }
  402. setEdgesDisplay(state, ignoreGuideLine){
  403. this.closed && this.edgeLabels.forEach(e=>e.setVisible(!!(state && e.shouldVisi)) )
  404. if(!ignoreGuideLine && this.measureType == 'Distance'){
  405. this.horEdgeLabel.visible = this.verEdgeLabel.visible = this.horGuideEdge.visible = this.verGuideEdge.visible = !!(state && this.shouldShowHorVerGuide)
  406. }
  407. }
  408. setSelected(state, hoverObject){//add
  409. //console.log('setSelected',state, hoverObject)
  410. hoverObject && (this.selectStates[hoverObject] = state)
  411. let absoluteState = false
  412. for(var i in this.selectStates){
  413. if(this.selectStates[i]){
  414. absoluteState = true; break;
  415. }
  416. }
  417. if(absoluteState){
  418. this.markers.forEach(e=>this.setMarkerSelected(e, 'hover', 'selectAll' ) )
  419. this.edges.forEach(e=>e.material = this.getLineMat('edgeSelect') )
  420. this.areaPlane && (this.areaPlane.material = planeMats.selected)
  421. //this.areaLabel && this.areaLabel.elem.addClass('highLight')
  422. //this.closed || this.edgeLabels.forEach(e=>e.elem.addClass('highLight') )
  423. this.setEdgesDisplay(true, hoverObject=="screenshot")
  424. this.areaLabel && setLabelHightState(this.areaLabel, true)
  425. this.closed || this.edgeLabels.forEach(e=>setLabelHightState(e, true) )
  426. }else{
  427. this.markers.forEach(e=>this.setMarkerSelected(e, 'unhover', 'selectAll' ))
  428. this.edges.forEach(e=>e.material = this.getLineMat('edgeDefault') )
  429. this.areaPlane && (this.areaPlane.material = planeMats.default)
  430. this.setEdgesDisplay(false, hoverObject=="screenshot")
  431. //this.areaLabel && this.areaLabel.elem.removeClass('highLight')
  432. //this.closed || this.edgeLabels.forEach(e=>e.elem.removeClass('highLight') )
  433. this.areaLabel && setLabelHightState(this.areaLabel, false)
  434. this.closed || this.edgeLabels.forEach(e=>setLabelHightState(e, false) )
  435. }
  436. this.selected = absoluteState
  437. if(hoverObject != 'byList'){
  438. //this.bus && this.bus.emit('highlight', this.selected)
  439. this.dispatchEvent({type:'highlight',state:this.selected})//列表高亮
  440. }
  441. viewer.dispatchEvent('content_changed')
  442. viewer.mapViewer && viewer.mapViewer.dispatchEvent('content_changed')
  443. }
  444. removeMarker(index ){
  445. super.removeMarker(index)
  446. this.points_datasets.splice(index, 1);
  447. this.dataset_points && this.dataset_points.splice(index, 1)
  448. this.coordinateLabels.splice(index, 1);
  449. let edgeIndex = index//(index === 0) ? 0 : (index - 1);
  450. if(this.edgeLabels[edgeIndex]){
  451. this.edgeLabels[edgeIndex].dispose()
  452. this.edgeLabels.splice(edgeIndex, 1);
  453. }
  454. this.update();
  455. this.dispatchEvent({type: 'marker_removed', measurement: this});
  456. }
  457. setPosition(index, position) {
  458. super.setPosition(index, position)
  459. let event = {
  460. type: 'marker_moved',
  461. measure: this,
  462. index: index,
  463. position: position.clone()
  464. };
  465. this.dispatchEvent(event);
  466. }
  467. dispose(){//add
  468. var labels = this.edgeLabels.concat(this.coordinateLabels)
  469. this.areaLabel && labels.push(this.areaLabel)
  470. labels.forEach(e=>e.dispatchEvent({type:'dispose'}))
  471. super.dispose()
  472. }
  473. getTotalDistance () {
  474. if (this.points.length === 0) {
  475. return 0;
  476. }
  477. let distance = 0;
  478. for (let i = 1; i < this.points.length; i++) {
  479. let prev = this.points[i - 1];
  480. let curr = this.points[i];
  481. let d = prev.distanceTo(curr);
  482. distance += d;
  483. }
  484. if (this.closed && this.points.length > 1) {
  485. let first = this.points[0];
  486. let last = this.points[this.points.length - 1];
  487. let d = last.distanceTo(first);
  488. distance += d;
  489. }
  490. return distance;
  491. }
  492. getAngleBetweenLines (cornerPoint, point1, point2) {
  493. let v1 = new THREE.Vector3().subVectors(point1, cornerPoint);
  494. let v2 = new THREE.Vector3().subVectors(point2, cornerPoint);
  495. // avoid the error printed by threejs if denominator is 0
  496. const denominator = Math.sqrt( v1.lengthSq() * v2.lengthSq() );
  497. if(denominator === 0){
  498. return 0;
  499. }else{
  500. return v1.angleTo(v2);
  501. }
  502. };
  503. getAngle (index) {
  504. if (this.points.length < 3 || index >= this.points.length) {
  505. return 0;
  506. }
  507. let previous = (index === 0) ? this.points[this.points.length - 1] : this.points[index - 1];
  508. let point = this.points[index];
  509. let next = this.points[(index + 1) % (this.points.length)];
  510. return this.getAngleBetweenLines(point, previous, next);
  511. }
  512. getCenter(/* update */){
  513. if(this.closed){
  514. return this.center.clone()
  515. }else{
  516. let center = this.points.reduce(function(total, currentValue ){
  517. return total.add(currentValue)
  518. }, new THREE.Vector3 )
  519. center.multiplyScalar(1/this.points.length)
  520. return center //求不出重心呜呜
  521. }
  522. }
  523. // updateAzimuth(){
  524. // // if(this.points.length !== 2){
  525. // // return;
  526. // // }
  527. // // const azimuth = this.azimuth;
  528. // // const [p0, p1] = this.points;
  529. // // const r = p0.distanceTo(p1);
  530. // }
  531. createGuideLine(){//add 辅助线
  532. var guideLine = LineDraw.createFatLine([ ],{mat:this.getLineMat('guide')} )
  533. guideLine.visible = false
  534. this.guideLine = guideLine
  535. this.add(guideLine);
  536. }
  537. createHorVerGuideLine(){//创建水平与垂直辅助线,仅距离测量有。
  538. var verGuideEdge = LineDraw.createFatLine([ ],{mat:this.getLineMat('guide')} )
  539. verGuideEdge.visible = false
  540. this.verGuideEdge = verGuideEdge
  541. verGuideEdge.name = 'verGuideEdge'
  542. var horGuideEdge = LineDraw.createFatLine([ ],{mat:this.getLineMat('guide')} )
  543. horGuideEdge.visible = false
  544. horGuideEdge.name = 'horGuideEdge'
  545. this.horGuideEdge = horGuideEdge
  546. this.add(this.verGuideEdge);
  547. this.add(this.horGuideEdge);
  548. //label:
  549. this.verEdgeLabel = this.createEdgeLabel('verGuideEdge')
  550. this.horEdgeLabel = this.createEdgeLabel('horGuideEdge')
  551. }
  552. createEdgeLabel(name, hasHoverEvent){
  553. const edgeLabel = new TextSprite(
  554. $.extend(hasHoverEvent ? mainLabelProp : subLabelProp,{sizeInfo: labelSizeInfo, name:name||'edgeLabel'})
  555. )
  556. if(hasHoverEvent){
  557. edgeLabel.addEventListener('mouseover',()=>{
  558. this.setSelected(true, 'edgeLabel')
  559. })
  560. edgeLabel.addEventListener('mouseleave',()=>{
  561. this.setSelected(false, 'edgeLabel')
  562. })
  563. edgeLabel.addEventListener('click',()=>{
  564. this.isNew || viewer.focusOnObject(this, 'measure')
  565. })
  566. }
  567. edgeLabel.visible = false
  568. edgeLabel.measure = this
  569. edgeLabel.sprite.material.depthTestWhenPick = true
  570. Potree.Utils.setObjectLayers(edgeLabel, 'measure' )
  571. this.add(edgeLabel)
  572. return edgeLabel
  573. }
  574. createAreaLabel(){
  575. const areaLabel = new TextSprite(
  576. $.extend({},mainLabelProp,{sizeInfo: labelSizeInfo, name:'areaLabel_', disToLine:0, fontsize:16*textSizeRatio} )
  577. )
  578. areaLabel.addEventListener('mouseover',()=>{
  579. this.setSelected(true, 'areaLabel')
  580. })
  581. areaLabel.addEventListener('mouseleave',()=>{
  582. this.setSelected(false, 'areaLabel')
  583. })
  584. areaLabel.addEventListener('click',()=>{
  585. this.isNew || viewer.focusOnObject(this, 'measure')
  586. })
  587. Potree.Utils.setObjectLayers(areaLabel, 'measure' )
  588. areaLabel.setVisible(false)
  589. return areaLabel;
  590. }
  591. getMarkerMaterial(type) {
  592. if(!markerMats){
  593. markerMats = {
  594. default: new DepthBasicMaterial($.extend({},lineDepthInfo,{
  595. transparent: !0,
  596. opacity: 1,
  597. map: texLoader.load(Potree.resourcePath+'/textures/pic_point_s32.png' ),
  598. useDepth:true ,
  599. mapScale: markerMapShrink
  600. })),
  601. select: new THREE.MeshBasicMaterial({
  602. transparent: !0,
  603. opacity: 1,
  604. depthTest:false,
  605. map: texLoader.load(Potree.resourcePath+'/textures/pic_point32.png'/* , null, null, { antialias: false } */),
  606. }),
  607. }
  608. Measure.markerMats = markerMats
  609. markerMats.select.map.repeat.set(1/markerMapShrink,1/markerMapShrink)
  610. markerMats.select.map.offset.set((markerMapShrink-1)/2/markerMapShrink, (markerMapShrink-1)/2/markerMapShrink)
  611. //markerMats.select.map.offset.set( -1.1 , -1.1 )
  612. }
  613. return markerMats[type]
  614. }
  615. getLineMat(type) {
  616. if(!Measure.lineMats){
  617. Measure.lineMats = {
  618. edgeDefault: LineDraw.createFatLineMat($.extend({},lineDepthInfo,{
  619. color: config.measure.default.color,
  620. lineWidth: config.measure.lineWidth,
  621. useDepth :true,
  622. dashWithDepth :true, // 只在被遮住的部分显示虚线,因为实线容易挡住label
  623. dashed :true,
  624. dashSize : 0.04,
  625. gapSize: 0.04,
  626. transparent: true,
  627. opacity: config.measure.default.opacity,
  628. depthTestWhenPick:true,
  629. })),
  630. edgeSelect: LineDraw.createFatLineMat($.extend({},lineDepthInfo,{
  631. color: config.measure.highlight.color,//'#f0ff00',
  632. dashSize: 0.5,
  633. gapSize: 0.2,
  634. lineWidth: config.measure.lineWidth ,
  635. transparent: true,
  636. opacity: config.measure.highlight.opacity
  637. })),
  638. guide: LineDraw.createFatLineMat($.extend({},lineDepthInfo,{
  639. color:config.measure.guide.color,
  640. dashSize: 0.1,
  641. gapSize: 0.02,
  642. dashed: true,
  643. lineWidth: config.measure.lineWidth/2
  644. }))
  645. }
  646. }
  647. return Measure.lineMats[type]
  648. }
  649. createAreaPlane(){
  650. planeMats || (planeMats = {
  651. default: new DepthBasicMaterial( $.extend({},lineDepthInfo,{
  652. color:color,
  653. side:THREE.DoubleSide,
  654. opacity:0.2,
  655. transparent:true,
  656. useDepth:true
  657. })),
  658. selected: new THREE.MeshBasicMaterial({
  659. color: color ,
  660. side:THREE.DoubleSide,
  661. opacity:0.3,
  662. transparent:true,
  663. })
  664. },Measure.planeMats = planeMats)
  665. return super.createAreaPlane(planeMats.default)
  666. }
  667. raycast (raycaster, intersects) {
  668. for (let i = 0; i < this.points.length; i++) {
  669. let marker = this.markers[i];
  670. marker.raycast(raycaster, intersects);
  671. }
  672. // recalculate distances because they are not necessarely correct
  673. // for scaled objects.
  674. // see https://github.com/mrdoob/three.js/issues/5827
  675. // TODO: remove this once the bug has been fixed
  676. for (let i = 0; i < intersects.length; i++) {
  677. let I = intersects[i];
  678. I.distance = raycaster.ray.origin.distanceTo(I.point);
  679. }
  680. intersects.sort(function (a, b) { return a.distance - b.distance; });
  681. };
  682. transformData(prop){
  683. if(prop.measureType == 'Point'){
  684. prop.showCoordinates = true,
  685. prop.closed = true,
  686. prop.maxMarkers = 1,
  687. prop.minMarkers = 1
  688. }else if(prop.measureType == 'Distance'){
  689. prop.showDistances = true,
  690. prop.showEdges = true,
  691. prop.maxMarkers = 2,
  692. prop.minMarkers = 2
  693. }else if(prop.measureType == 'MulDistance'){//new
  694. prop.showDistances = true,
  695. prop.showEdges = true,
  696. prop.minMarkers = 2
  697. }else if(prop.measureType == 'Ver MulDistance'){
  698. prop.showDistances = true,
  699. prop.atPlane = true,
  700. prop.showEdges = true,
  701. prop.minMarkers = 2
  702. prop.faceDirection = "vertical"
  703. prop.unableDragAtMap = true
  704. }else if(prop.measureType == 'Hor MulDistance'){
  705. prop.showDistances = true,
  706. prop.atPlane = true,
  707. prop.showEdges = true,
  708. prop.minMarkers = 2
  709. prop.faceDirection = "horizontal"
  710. }else if(prop.measureType == 'Ver Distance'){
  711. prop.showDistances = true,
  712. prop.showEdges = true,
  713. prop.maxMarkers = 2,
  714. prop.minMarkers = 2,
  715. prop.faceDirection = "vertical"
  716. prop.unableDragAtMap = true
  717. }else if(prop.measureType == 'Hor Distance'){
  718. prop.showDistances = true,
  719. prop.showEdges = true,
  720. prop.maxMarkers = 2,
  721. prop.minMarkers = 2,
  722. prop.faceDirection = "horizontal"
  723. }else if(prop.measureType == 'Area'){
  724. prop.showDistances = true,
  725. prop.atPlane = true,
  726. prop.showEdges = true,
  727. prop.closed = true,
  728. prop.minMarkers = 3
  729. }else if(prop.measureType == 'Hor Area'){
  730. prop.showDistances = true,
  731. prop.atPlane = true,
  732. prop.showEdges = true,
  733. prop.closed = true,
  734. prop.minMarkers = 3
  735. prop.faceDirection = "horizontal"
  736. }else if(prop.measureType == 'Ver Area'){
  737. prop.showDistances = true,
  738. prop.atPlane = true,
  739. prop.showEdges = true,
  740. prop.closed = true,
  741. prop.minMarkers = 3
  742. prop.faceDirection = "vertical"
  743. prop.unableDragAtMap = true
  744. }else if(prop.measureType == 'Rect Area'){
  745. prop.showDistances = true,
  746. prop.atPlane = true,
  747. prop.showEdges = true,
  748. prop.closed = true,
  749. prop.minMarkers = 4
  750. prop.maxMarkers = 4
  751. }else if(prop.measureType == 'Hor Rect Area'){
  752. prop.showDistances = true,
  753. prop.atPlane = true,
  754. prop.showEdges = true,
  755. prop.closed = true,
  756. prop.minMarkers = 4
  757. prop.maxMarkers = 4
  758. prop.isRect = true
  759. prop.faceDirection = "horizontal"
  760. }else if(prop.measureType == 'Ver Rect Area'){
  761. prop.showDistances = true,
  762. prop.atPlane = true,
  763. prop.showEdges = true,
  764. prop.closed = true,
  765. prop.minMarkers = 4
  766. prop.maxMarkers = 4
  767. prop.isRect = true
  768. prop.faceDirection = "vertical"
  769. prop.unableDragAtMap = true
  770. }
  771. if(prop.atPlane && prop.closed){ //atPlane在同一平面上
  772. prop.showArea = true
  773. }
  774. super.transformData(prop)
  775. }
  776. setUnitSystem(unitSystem){
  777. //console.log(this.name +':' +this.unitSystem)
  778. if(unitSystem != this.unitSystem){
  779. if(unitSystem == "metric"){
  780. }else if(unitSystem == 'imperial'){
  781. }
  782. this.unitSystem = unitSystem
  783. this.update()
  784. }
  785. }
  786. reDraw(restMarkerCount=0){//重新开始画
  787. super.reDraw(restMarkerCount)
  788. if(this.measureType == 'Distance'){
  789. this.shouldShowHorVerGuide = false
  790. this.setEdgesDisplay(false)
  791. }
  792. if(this.showArea){
  793. this.area = {value:0};
  794. this.areaLabel && this.areaLabel.setVisible(false)
  795. }
  796. viewer.inputHandler.dispatchEvent( {type:'isMeasuring', v:true, cause:'reDraw'} )
  797. }
  798. }
  799. function setLabelHightState(label, state){
  800. if(state){
  801. let color = new THREE.Color(Potree.config.measure.highlight.color)
  802. //label.sprite.material.opacity = config.measure.highlight.opacity
  803. //label.setBackgroundColor({r:255*color.r, g:255*color.g, b:255*color.b, a:config.measure.highlight.opacity})
  804. label.sprite.material.useDepth = false;
  805. //label.textColor = {r: this.color.r*255, g: this.color.g*255, b: this.color.b*255, a: 1}
  806. }else{
  807. //label.setBackgroundColor({r: this.color.r*255, g: this.color.g*255, b: this.color.b*255, a:config.measure.default.opacity})
  808. label.sprite.material.useDepth = true
  809. //label.sprite.material.opacity = 0.98
  810. //label.textColor = {r: 255, g: 255, b: 255, a: 1}
  811. }
  812. label.updateTexture()
  813. }
  814. /* function setLabelHightState(label, state){
  815. if(state){
  816. label.setBackgroundColor({r: highlightColor.r*255, g: highlightColor.g*255, b: highlightColor.b*255, a:config.measure.highlight.labelOpacity})
  817. label.sprite.material.useDepth = false;
  818. }else{
  819. label.setBackgroundColor(mainLabelProp.backgroundColor)
  820. label.sprite.material.useDepth = true
  821. }
  822. label.updateTexture()
  823. //label.sprite.material.needsUpdate = true
  824. }
  825. */
  826. function createCircleRadiusLabel(){
  827. const circleRadiusLabel = new TextSprite("");
  828. circleRadiusLabel.setTextColor({r: 140, g: 250, b: 140, a: 1.0});
  829. circleRadiusLabel.setBorderColor({r: 0, g: 0, b: 0, a: 1.0});
  830. circleRadiusLabel.setBackgroundColor({r: 0, g: 0, b: 0, a: 1.0});
  831. circleRadiusLabel.fontsize = 16;
  832. circleRadiusLabel.material.depthTest = false;
  833. circleRadiusLabel.material.opacity = 1;
  834. circleRadiusLabel.visible = false;
  835. return circleRadiusLabel;
  836. }
  837. function createCircleRadiusLine(){
  838. /* const lineGeometry = new LineGeometry();
  839. lineGeometry.setPositions([
  840. 0, 0, 0,
  841. 0, 0, 0,
  842. ]);
  843. const lineMaterial = new LineMaterial({
  844. color: 0xff0000,
  845. lineWidth: 2,
  846. resolution: new THREE.Vector2(1000, 1000),
  847. gapSize: 1,
  848. dashed: true,
  849. });
  850. lineMaterial.depthTest = false;
  851. const circleRadiusLine = new Line2(lineGeometry, lineMaterial);*/
  852. var circleRadiusLine = LineDraw.createFatLine([ ],{
  853. color:0xff0000,
  854. dashSize: 0.5,
  855. gapSize: 0.2,
  856. lineWidth: config.measure.lineWidth
  857. })
  858. circleRadiusLine.visible = false;
  859. return circleRadiusLine;
  860. }
  861. function createCircleLine(){
  862. const coordinates = [];
  863. let n = 128;
  864. for(let i = 0; i <= n; i++){
  865. let u0 = 2 * Math.PI * (i / n);
  866. let u1 = 2 * Math.PI * (i + 1) / n;
  867. let p0 = new THREE.Vector3(
  868. Math.cos(u0),
  869. Math.sin(u0),
  870. 0
  871. );
  872. let p1 = new THREE.Vector3(
  873. Math.cos(u1),
  874. Math.sin(u1),
  875. 0
  876. );
  877. coordinates.push(
  878. p0,
  879. p1
  880. );
  881. }
  882. /* const geometry = new LineGeometry();
  883. geometry.setPositions(coordinates);
  884. const material = new LineMaterial({
  885. color: 0xff0000,
  886. dashSize: 5,
  887. gapSize: 2,
  888. lineWidth: 2,
  889. resolution: new THREE.Vector2(1000, 1000),
  890. });
  891. material.depthTest = false;
  892. const circleLine = new Line2(geometry, material);
  893. circleLine.visible = false;
  894. circleLine.computeLineDistances();*/
  895. var circleLine = LineDraw.createFatLine(coordinates,{
  896. color: 0xff0000,
  897. dashSize: 0.5,
  898. gapSize: 0.2,
  899. lineWidth: config.measure.lineWidth
  900. })
  901. return circleLine;
  902. }
  903. /* function createCircleCenter(){
  904. const sg = new THREE.markerGeometry(1, 32, 32);
  905. const sm = new THREE.MeshNormalMaterial();
  906. const circleCenter = new THREE.Mesh(sg, sm);
  907. circleCenter.visible = false;
  908. return circleCenter;
  909. } */
  910. function createLine(){
  911. const line = LineDraw.createFatLine([ ],{
  912. color: 0xff0000,
  913. dashSize: 0.5,
  914. gapSize: 0.2,
  915. lineWidth: config.measure.lineWidth
  916. })
  917. return line;
  918. }
  919. function createCircle(){
  920. const coordinates = [];
  921. let n = 128;
  922. for(let i = 0; i <= n; i++){
  923. let u0 = 2 * Math.PI * (i / n);
  924. let u1 = 2 * Math.PI * (i + 1) / n;
  925. let p0 = new THREE.Vector3(
  926. Math.cos(u0),
  927. Math.sin(u0),
  928. 0
  929. );
  930. let p1 = new THREE.Vector3(
  931. Math.cos(u1),
  932. Math.sin(u1),
  933. 0
  934. );
  935. coordinates.push(
  936. p0,
  937. p1
  938. );
  939. }
  940. var line = LineDraw.createFatLine(coordinates,{
  941. color: 0xff0000,
  942. dashSize: 0.5,
  943. gapSize: 0.2,
  944. lineWidth: config.measure.lineWidth
  945. })
  946. return line;
  947. }
  948. /* function createAzimuth(){
  949. const azimuth = {
  950. label: null,
  951. center: null,
  952. target: null,
  953. north: null,
  954. centerToNorth: null,
  955. centerToTarget: null,
  956. centerToTargetground: null,
  957. targetgroundToTarget: null,
  958. circle: null,
  959. node: null,
  960. };
  961. const sg = new THREE.markerGeometry(1, 32, 32);
  962. const sm = new THREE.MeshNormalMaterial();
  963. {
  964. const label = new TextSprite("");
  965. label.setTextColor({r: 140, g: 250, b: 140, a: 1.0});
  966. label.setBorderColor({r: 0, g: 0, b: 0, a: 1.0});
  967. label.setBackgroundColor({r: 0, g: 0, b: 0, a: 1.0});
  968. label.fontsize = 16;
  969. label.material.depthTest = false;
  970. label.material.opacity = 1;
  971. azimuth.label = label;
  972. }
  973. azimuth.center = new THREE.Mesh(sg, sm);
  974. azimuth.target = new THREE.Mesh(sg, sm);
  975. azimuth.north = new THREE.Mesh(sg, sm);
  976. azimuth.centerToNorth = createLine();
  977. azimuth.centerToTarget = createLine();
  978. azimuth.centerToTargetground = createLine();
  979. azimuth.targetgroundToTarget = createLine();
  980. azimuth.circle = createCircle();
  981. azimuth.node = new THREE.Object3D();
  982. azimuth.node.add(
  983. azimuth.centerToNorth,
  984. azimuth.centerToTarget,
  985. azimuth.centerToTargetground,
  986. azimuth.targetgroundToTarget,
  987. azimuth.circle,
  988. azimuth.label,
  989. azimuth.center,
  990. azimuth.target,
  991. azimuth.north,
  992. );
  993. return azimuth;
  994. } */
  995. /*
  996. */