1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204 |
- import * as THREE from "../../../../libs/three.js/build/three.module.js";
- import {TextSprite} from "../TextSprite.js";
- import {Utils} from "../../../utils.js";
- import Label from "../Label.js";
- import {LineDraw , MeshDraw} from "../../utils/DrawUtil.js";
- import math from "../../utils/math.js";
- import DepthBasicMaterial from "../../materials/DepthBasicMaterial.js";
- import Sprite from '../Sprite.js'
- import {config} from '../../settings.js'
- import browser from "../../utils/browser.js";
- import {ctrlPolygon} from './ctrlPolygon.js'
- import {Measure} from './Measure.js'
- import CursorDeal from "../../utils/CursorDeal.js";
- let texLoader = new THREE.TextureLoader()
-
- const labelSizeInfo = {width2d:180} //稍微小点防止字体模糊
-
- const titleLineHeight = 2
-
-
- const arrowCountMax = 1000; //箭头总数不能超过这个值。 The count value passed into the constructor represents the maximum number of instances of this mesh. You can change the number of instances at runtime to an integer value in the range [0, count].If you need more instances than the original count value, you have to create a new InstancedMesh.
- let lastArrowCamPos, lastArrowCount = 0
-
- const depthProps = {
- useDepth : true ,
- //startClipDis : 0.5,
- clipDistance : 2,//消失距离
- //startOcclusDis: 0.5,
- occlusionDistance: 0.7,//变为backColor距离
- maxOcclusionFactor:0.9,
- maxClipFactor:1
- }
- const planeGeo = new THREE.PlaneBufferGeometry(1,1)
- const voidGeometry = new THREE.BufferGeometry()
- let markerMats
- const getMarkerMat = function (name) {
- if(!markerMats){
- markerMats = {
- default: new DepthBasicMaterial( $.extend({}, depthProps,{
- transparent: true,
- map: texLoader.load( Potree.resourcePath+'/textures/dot_n.png'),
- opacity:0.9
- })),
- drag : new DepthBasicMaterial({
- transparent: true, useDepth:false,
- map: texLoader.load( Potree.resourcePath+'/textures/dot_s.png'),
- }),
- delete : new DepthBasicMaterial({
- transparent: true, useDepth:false,
- map: texLoader.load( Potree.resourcePath+'/textures/dot_r.png'),
- }),
- adding : new DepthBasicMaterial({
- transparent: true, opacity:0.3, useDepth:false,
- map: texLoader.load( Potree.resourcePath+'/textures/dot_n.png'),
- }),
- }
- for(let i in markerMats){markerMats[i].map.anisotropy = 4}
- }
- return markerMats[name]
- }
-
- const getMeshQuaInPath = (lineDir)=>{
- const quaBase = new THREE.Quaternion().setFromEuler(new THREE.Euler(-Math.PI/2,0, Math.PI/2))
- return math.getQuaFromPosAim( new THREE.Vector3, lineDir ).multiply(quaBase)
- }
- const getEndCaps = (function () {
- let endCap, map
- return function(path){
- if(!endCap){
- map = texLoader.load(Potree.resourcePath+'/textures/whiteCircle.png')
- map.anisotropy = 3
- map.repeat.set(0.5, 1);
- //map.magFilter = THREE.NearestFilter
- let mesh = new THREE.Mesh(planeGeo )
- mesh.scale.x = 0.5;
- mesh.position.x = -0.25
- //Potree.Utils.setObjectLayers(mesh,'measure')
- endCap = new THREE.Object3D()
- endCap.add(mesh)
- }
- let endCaps = []
- let material = path.edge.material.clone()
- material.map = map
- delete material.defines.mapOverlay
- for(let i=0;i<2;i++){
- let cap = endCap.clone()
- cap.children[0].material = material
- path.add(cap)
- endCaps.push(cap)
- }
-
- return endCaps
- }
- })()
- const setMarkerScale = (marker, halfPathWidth)=>{
- let s = halfPathWidth * 2.9
- marker.scale.set(s,s,s)
- }
- let fakeMarker
- let lastFadeTime
- let showFakeMarker = (path, position)=>{//添加marker时指示位置
- if(!fakeMarker){
- fakeMarker = new THREE.Mesh(planeGeo, getMarkerMat('adding'))
- fakeMarker.name = 'fakeMarker'
- fakeMarker.renderOrder = Potree.config.renderOrders.path.marker
-
- }
- setMarkerScale(fakeMarker, path.halfPathWidth)
- path.add(fakeMarker)
- fakeMarker.position.copy(position)
- Potree.Utils.updateVisible(fakeMarker,'add',true)
- viewer.dispatchEvent('content_changed')
- return fakeMarker
- }
-
- let hideFakeMarker = ()=>{
-
- fakeMarker && Potree.Utils.updateVisible(fakeMarker,'add',false)
- lastFadeTime = Date.now()
- }
-
-
- export class Path extends ctrlPolygon{
- constructor (prop) {
-
- super('Path', prop);
-
- this.zPlaneWhenNoIntersect = 0 //允许不在model上,直接绘制此高度的水平面上
- this.markerLabels = [];
- this.points_datasets || (this.points_datasets = []) //存每个点是哪个数据集
- this.hoverStates = {}
- this.selectStates = {}
- this.setFadeFar(null)
- this.geoPoints = []
- {
- let group = new THREE.Object3D; group.name = 'titleGroup'
- this.titleLabel = new TextSprite(Object.assign({}, depthProps,{
- text: '', sizeInfo:{width2d:200},
- textColor:{r:255,g:255,b:255,a:1.0},
- backgroundColor:{r:0,g:0,b:0,a:0.5},
- borderRadius: 6,
- fontsize: this.fontsize || 13,
- renderOrder : Potree.config.renderOrders.path.label,
- pickOrder: Potree.config.renderOrders.path.label,
- clipDistance : 10,
- fadeFar: this.fadeFar,
- transform2Dpercent:{x:0,y:0.5}, //向上移动一半
- maxLineWidth: 300,
- textAlign: Potree.settings.isOfficial && 'left'
- }))
- this.titleLabel.sprite.material.depthTest = false
- let line = LineDraw.createFatLine([new THREE.Vector3(0,0,0), new THREE.Vector3(0,0,titleLineHeight)], Object.assign({},depthProps,{color: '#ffffff', lineWidth: 1, transparent:true, fadeFar: this.fadeFar}))
- line.renderOrder = Potree.config.renderOrders.line
- group.add(line)
- group.add(this.titleLabel)
- this.titleLabel.position.z = titleLineHeight
- this.add(group);
- this.setTitleVisi(this.titleLabel.parent, false, 'noPoint')
- this.setTitle(Potree.settings.isOfficial ? '' : 'title' )
-
-
- line.addEventListener('mouseover',(e)=>{
- this.editEnable && CursorDeal.add('hoverGrab')
- });
- line.addEventListener('startDragging',(e)=>{
- this.editEnable && CursorDeal.add('grabbing')
- });
- line.addEventListener('drop',(e)=>{
- this.editEnable && CursorDeal.remove('grabbing')
- });
- line.addEventListener('mouseleave',(e)=>{
- this.editEnable && CursorDeal.remove('hoverGrab')
-
- });
- line.addEventListener('drag',(e)=>{
- if(this.editEnable){//一旦用户拖动了title,title就固定了,不再随着path居中
- let position
- if(e.intersect?.location){
- position = e.intersect?.location
- }else{
- let {x,y} = Potree.Utils.getPointerPosAtHeight(0,e.pointer)
- position = new THREE.Vector3(x,y,0)
- }
- this.updateTitlePos(position)
- this.dispatchEvent({type:'titlePosChanged', position , root:e.intersect?.pointcloud || e.intersect?.object})
- }
- });
- }
-
- {//和measure不同的是它的边是连在一起的一整条
- this.edge = new THREE.Mesh(voidGeometry, new DepthBasicMaterial( $.extend({}, depthProps,{color: this.color , /* opacity: 0.6, */side:2,/* transparent:true, */fadeFar: this.fadeFar})))
- //new THREE.MeshBasicMaterial({depthWrite:false, transparent:true, color:this.color || '#fff', opacity:0.5, side:2}))
- this.edge.material.defines.mapOverlay = true
- this.add(this.edge)
- this.edge.renderOrder = Potree.config.renderOrders.path.edge, //和tag的一样,但为何遮不住它?好在一般底下有一层地面能遮住
- this.edge.name = 'pathEdge'
- let addHoverEvent = ()=>{
- let mouseover = (e) => {
- if(this.isNew)return
- this.setSelected('hover')
- this.hoverStates.edge = true
- if(this.addOrRemovePoint && !this.isNew){
- CursorDeal.add('pen_addPoint')
- }
- };
- let mouseleave = (e) => {
- if(this.isNew)return
- this.setSelected('unhover')
- this.hoverStates.edge = false
- CursorDeal.remove('pen_addPoint')
- hideFakeMarker()
- };
-
- this.edge.addEventListener('mouseover', mouseover);
- this.edge.addEventListener('mouseleave', mouseleave);
-
- this.edge.addEventListener('mousemove', (e)=>{
- if(this.addOrRemovePoint && !this.isNew && !this.hoverStates.marker){
- let { point } = this.getPosByIntersect(e, 'onlyPoint')
- showFakeMarker(this, point)
- }
- });
-
- this.edge.addEventListener('click',(e)=>{
- let now = Date.now()
- if(now - this.lastDropTime<100 || this.isNew || e.button !== THREE.MOUSE.LEFT)return ;
-
- if(this.addOrRemovePoint ){
- let {index, prevIndex, point} = this.getPosByIntersect(e)
- viewer.measuringTool.history.beforeChange(this)
- this.addMarker({
- index,
- point,
- dataset_point: this.dataset_points && new THREE.Vector3 , //初始化
- points_dataset : this.points_datasets[prevIndex] //使用前一个的
- })
-
- this.updateDatasetBelong(index) //获取dataset_point
- viewer.measuringTool.history.afterChange(this)
- this.updateEdge()
- this.hideArrowUntilUpdate()
- this.dispatchEvent('changed')
- }else{
- this.isNew || viewer.measuringTool.isAdding || this.setSelected('click') //viewer.focusOnObject(this, 'measure') //正在添加测量线时不要focus其他线(容易误触)
- }
- e.consume() //防止后续双击
- })
- }
- this.edge.addEventListener('addHoverEvent', addHoverEvent, {once:true});
- if(!this.isNew){
- this.edge.dispatchEvent('addHoverEvent')
- }
-
- }
-
-
- {
-
- this.endCaps = getEndCaps(this) //端点处的半圆
-
- }
-
-
- this.addEventListener('marker_dropped',(e)=>{
- this.updateDatasetBelong(e.index)
- })
- this.setPathWidth(prop.width || 0.2)
- this.setColor(prop.color || '#fff')
-
-
- this.setEditEnable(true)
-
- this.lastDropTime = 0
- //Potree.Utils.setObjectLayers(this, 'measure' )
-
- if(!Potree.settings.isOfficial){
- this.setAddOrRemPoint(true)
- this.addEventListener('createDone',()=>{
- this.setAddOrRemPoint(false)
- })
- this.setArrowDisplay(true)
- }
-
-
- this.initData(prop)
-
-
- }
-
-
- updateTitlePos(pos){
- if(pos){
- this.fixedTitlePos = pos
- }else{
- if(!this.fixedTitlePos){ //居中
- pos = this.getCenter()
- }
- }
-
- if(pos){
- this.titleLabel.parent.position.copy(pos)
- this.titleLabel.updatePose()
- }
- }
-
-
-
-
- getPosByIntersect(e, type){//intersect落在线上的位置,以及在哪两个点之间
-
- if( !Potree.settings.pathSmooth ){
- let prevIndex = Math.floor(e.hoveredElement.faceIndex / 2) //端点1(可能是最后一个)
- let nextIndex = this.getIndex(prevIndex, 1) //端点2(可能是第一个)
- let index = prevIndex + 1 //新点在端点1后
- let point = math.getFootPoint(e.hoveredElement.point, this.points[prevIndex], this.points[nextIndex] );
- return {index, prevIndex, point }
- }else{
-
- let prevIndex0 = Math.floor(e.hoveredElement.faceIndex / 2) //所在的mesh片段的端点1
- let nextIndex0 = prevIndex0 + 1 //所在的mesh片段的端点2
-
- let point = math.getFootPoint(e.hoveredElement.point, this.geoPoints[prevIndex0], this.geoPoints[nextIndex0] ); //新点位置
- if(type == 'onlyPoint')return {point}
-
- let prevIndex,nextIndex
- let count = this.points.length - 1
- for(let i=0;i<count;i++){
- if(prevIndex == void 0 && i / count <= this.UtoTMapArr[prevIndex0] && (i+1) / count > this.UtoTMapArr[prevIndex0]){
- prevIndex = i //该片段端点1在原先points中哪个点之后(可包含)
- }
- if(nextIndex == void 0 && i / count <= this.UtoTMapArr[nextIndex0] && (i+1) / count > this.UtoTMapArr[nextIndex0]){
- nextIndex = i //该片段端点2在原先points中哪个点之后(可包含)
- }
- }
- if(nextIndex == void 0){//最后一个点
- nextIndex = count-1
- }
-
- if(prevIndex != nextIndex){//端点在不同points区间, 需要判断intersect的究竟在哪个区间
-
- //console.log('跨点', prevIndex, nextIndex)
- this.geoPoints[prevIndex]
- let lengths = [] //geo端点间包含points哪些点
- let j=prevIndex;
- let A = this.geoPoints[prevIndex0]
- let B = this.geoPoints[nextIndex0]
- let APlen = A.distanceTo(point); // 端点->点击点
- let AB = new THREE.Vector3().subVectors(B,A)
- let searchIndex
- while(j <= nextIndex){ //根据APlen长度算区间
- let len = AB.clone().normalize().dot(new THREE.Vector3().subVectors(this.points[j+1], A)) //在AB的投影长度
- if(len > APlen){
- searchIndex = j
- break
- }
- j++
- }
- searchIndex == void 0 && (searchIndex = nextIndex) //最后一个点之后
- prevIndex = searchIndex
- }
- nextIndex = prevIndex + 1
- //console.log(prevIndex, nextIndex)
- let index = prevIndex + 1 //新点在端点1后
- return {index, prevIndex, point }
- }
- }//如果后续还出现index错误的问题,可以改为绘制时用折线,完成后用曲线。
- createMarkerLabel(text, hasHoverEvent){
-
- const label = new TextSprite(
- $.extend( {}, depthProps, {
- sizeInfo: labelSizeInfo, name:'markerTitle',
- text : "" , fontsize: this.fontsize || 12,
- renderOrder : Potree.config.renderOrders.path.label,
- pickOrder: Potree.config.renderOrders.path.label,
- fontWeight:'',//thick
- backgroundColor:{r:255,g:255,b:255,a:0.5},
- textColor:{r:0,g:0,b:0,a:1},
- fadeFar: this.fadeFar,
- maxLineWidth: 300,
- transform2D: {x:0,y:0.2}, //朝上偏移一些 配合最外层z混合增高
- textAlign: Potree.settings.isOfficial && 'left'
- })
- )
- if(hasHoverEvent){
- label.addEventListener('mouseover',()=>{
- this.setSelected(true, 'label')
- })
- label.addEventListener('mouseleave',()=>{
- this.setSelected(false, 'label')
- })
- label.addEventListener('click',()=>{
- this.isNew || viewer.measuringTool.isAdding || viewer.focusOnObject(this, 'measure')
- })
- }
- //label.measure = this
- //label.sprite.material.depthTestWhenPick = true
- //Potree.Utils.setObjectLayers(label, 'measure' )
- this.add(label)
- return label
- }
-
- updateMarker(marker, pos){
- marker.position.copy(pos);
- marker.position.z += 0.01
-
- let index = this.markers.indexOf(marker)
-
- if(this.markerLabels[index]){
- this.markerLabels[index].position.copy(pos)
- this.markerLabels[index].position.z += 0.3 //混合增高
- this.markerLabels[index].updatePose()
- }
-
-
- }
-
- setPathWidth(w){
- let v = w / 2
- if(this.halfPathWidth == v)return
- this.halfPathWidth = v
- this.markers.forEach(e=>setMarkerScale(e, this.halfPathWidth))
- this.updateEdge()
- this.editEnable || this.updateEndCaps()
- this.updateArrowRepeat()
- viewer.dispatchEvent('content_changed')
-
- this.hideArrowUntilUpdate()
-
- }
- setColor(color){
- if(this.color == color)return
- this.color = color
- let c = new THREE.Color().set(this.color)
- this.titleLabel.setTextColor({r: c.r*255, g:c.g*255, b:c.b*255, a:0.5})
- this.updateSelectStyle() //apply color
-
- viewer.dispatchEvent('content_changed')
- }
-
- updateEdge(){
- if(this.lastUpdatePoints_ && Potree.Common.ifSame(this.lastUpdatePoints_,this.points) && this.halfPathWidth == this.lastHalfPathWidth) return //没变 不更新
- //this.edge.geometry = MeshDraw.getExtrudeGeo(edgeExtrudePoints, null, {extrudePath: this.points, openEnded:true, shapeDontClose:true/* , dontSmooth:true, steps: this.points.length-1 */})
- //getExtrudeGeo是平滑过的曲线,和设计不一样,且容易翻转,转角有时候细分过少
-
- //只允许path水平放置
- let geo
- let points = this.getDifferentPoint(this.points)
- let count = points.length
- this.edge.geometry.dispose()
-
- if(count <= 1){
- geo = voidGeometry
- this.geoPoints = []
- this.curve = null
- }else{
- if(Potree.settings.pathSmooth){//使用曲线
- let curve = this.curve = new THREE.CatmullRomCurve3(points, false )
- curve.UtoTMapArr = [] //用于存储 getSpacedPoints得到的点对应points的百分比对应
- let oldCount = count
- count = Math.max(2, Math.round(this.getTotalDistance() * 200)), points = curve.getSpacedPoints(count-1) //拆分为更密集的点
-
- /* //window.arcLengthDivisions && (curve.arcLengthDivisions = arcLengthDivisions) //默认200,但改为1也没变化呀
- let oldCount = count
- //减少点数(拐弯的部分紧凑些,直线部分宽松些):
- let lastVec
- let newPoints = []
- this.UtoTMapArr = []
- let pointIndexs = []//找出新点中对应原先控制点的index,这些点必须加入拐点,否则会出现控制点偏移path(当它所在部分接近直线时)
- for(let n=1;n<oldCount-1;n++){
- pointIndexs.push( curve.UtoTMapArr.findIndex(e=>e>= n / (oldCount-1) ) )
- }
- for(let i=0;i<count;i++){
- let point = points[i];
- let last = points[i-1]
- let next = points[i+1]
-
- if(i == 0 || i == count-1 ) {
- newPoints.push(point) //直接加入
- this.UtoTMapArr.push(i == 0 ? 0 : 1)
- }
- if(i<count-1){
- let curVec = new THREE.Vector3().subVectors(next,point)
- if(!lastVec) lastVec = curVec
- if(i>1){// 和上一个加入点的vec之间的夹角如果过大就加入
- if(pointIndexs.includes(i) || curVec.angleTo(lastVec) > 0.05){//最小角度
- newPoints.push(point)
- this.UtoTMapArr.push(curve.UtoTMapArr[i])
- lastVec = curVec
- }
- }
- }
- }
- points = newPoints
- */
-
- let result = MeshDraw.lessCurvePoints(points, oldCount, 0.05, curve.UtoTMapArr)
- this.UtoTMapArr = result.newUtoTMapArr
- points = result.newPoints
- count = points.length
-
- //delete curve.UtoTMapArr
- }
- let lastSideVec
- let posArr = [], faceArr = [], uvArr = []
- let gatherLen = 0 //累加长度
- this.geoPoints = points
- //根据点序列计算geometry
-
- for(let i=0; i<count; i++){
-
- let O = points[i] //该点
- let A = points[i-1] //上一个点
- let B = points[i+1] //下一个点
-
-
- let p1, p2 //该点两边的折点
- let sideVec //该点两边对应的向量
- let uvX
- if(i==0 || i==count-1){
- sideVec = new THREE.Vector3().copy(math.getNormal2d({p1: i==0 ? B : O, p2: i==0 ? O : A})).setZ(0).multiplyScalar(this.halfPathWidth) //垂线
- uvX = i==0 ? 0 : 1 //percent of length
- }else{
- let OA = new THREE.Vector3().subVectors(A,O).setZ(0).normalize() //只保证俯视角度正确。(如果两点有高度差,该段四个点不会在同一平面,看起来有扭转,有的地方会肥大,但从俯视角度看是正确的。)
- let OB = new THREE.Vector3().subVectors(B,O).setZ(0).normalize()
- let angle = math.getAngle(OA, OB, 'z')
-
- if(math.closeTo(angle,0,1e-4) || math.closeTo(angle,Math.PI,1e-4) || math.closeTo(angle, -Math.PI,1e-4)){ //这时候直接加两个向量算出的平分线不准,故直接找垂线
- sideVec = new THREE.Vector3().copy(math.getNormal2d({p1: O, p2: A})).setZ(0).multiplyScalar(this.halfPathWidth) //垂线
- //console.log('接近0或180',angle, sideVec)
- }else{
- let midVecLength = this.halfPathWidth / Math.sin(angle/2)
- sideVec = new THREE.Vector3().addVectors(OA,OB).normalize().multiplyScalar(midVecLength) //角平分线 ( 和上一个方向保持在同一侧,故而顺时针和逆时针方向不同 )
- }
- gatherLen += O.distanceTo(A)
- uvX = gatherLen / this.totalLength //目前为止的长度占比
- }
- let P1 = new THREE.Vector3().addVectors(O, sideVec)
- let P2 = new THREE.Vector3().subVectors(O, sideVec)
-
- posArr.push(P1, P2)
- uvArr.push({x:uvX,y:0},{x:uvX,y:1})
-
- if(i<count-1){
- faceArr.push([i*2,i*2+1,i*2+3],[i*2,i*2+3,i*2+2])//每一段两个三角形 013 032
- }
- lastSideVec = sideVec
- }
- geo = MeshDraw.createGeometry(posArr, faceArr, uvArr)
- this.updateArrowRepeat()
- }
- this.edge.geometry = geo
-
- this.lastHalfPathWidth = this.halfPathWidth
- this.lastUpdatePoints_ = this.points.map(e=>e.clone())
-
- }//不使用curve的曲线是因为那种geo点数较多,且改的话不知道updateArrows怎么改,除非变为贴图,但就和需求中需要调节间距违背。
-
-
-
-
- update(options={}) {
- if(options.index == -1)return
-
- super.update(options) //updateEdge marker
- this.points.length<=1 && this.updateEdge() //补偿上一句
- this.editEnable || this.updateEndCaps()
- {
- let oldVisi = this.titleLabel.parent.visible
- this.setTitleVisi(this.titleLabel.parent, this.markers.length>0, 'noPoint')
- if(this.titleLabel.parent.visible && !oldVisi)this.titleLabel.updatePose()
- }
-
- this.updateTitlePos()
- };
-
-
-
-
- addMarker(o={}) {
- var index = o.index == void 0 ? this.points.length : o.index //要当第几个
-
- let marker = new THREE.Mesh(planeGeo, getMarkerMat('default'))
- marker.name = 'marker'
- setMarkerScale(marker,this.halfPathWidth)
- marker.markerSelectStates = {}
- //Potree.Utils.setObjectLayers(marker, 'measure' )
- marker.renderOrder = Potree.config.renderOrders.path.marker
-
- marker.addEventListener('mouseover',(e)=>{
- if(this.addOrRemovePoint && !this.isNew){
- CursorDeal.add('pen_delPoint')
- //hideFakeMarker()
- }
- this.hoverStates.marker = marker
- }/* ,{importance:1} */)
-
- marker.addEventListener('mouseleave',(e)=>{
- CursorDeal.remove('pen_delPoint')
- marker == this.hoverStates.marker && (this.hoverStates.marker = null)
- })
- marker.addEventListener('click',(e)=>{
- if(this.isNew || e.button !== THREE.MOUSE.LEFT )return
- if(this.addOrRemovePoint ){//点击删除点
- this.removePoint(this.markers.indexOf(marker))
-
- }else{//点击选中点
- this.dispatchEvent({type:'markerSelect', marker})
- this.setMarkerSelected(marker, 'click' );
-
- setTimeout(()=>{
- viewer.addEventListener('global_click', (e)=>{ //点击空白处取消全部
- if(e.clickElement?.oriObject == marker)return
- this.dispatchEvent({type:'markerSelect', marker, cancel:true})
- this.setMarkerSelected(marker, 'unclick' );
- }, {once:true} )
- },10)
- }
- e.consume()
- })
-
- marker.addEventListener('startDragging',(e)=>{
- this.isNew || this.setMarkerSelected(marker, 'click' ); //选中
- this.isNew || viewer.measuringTool.history.beforeChange(this)
- this.arrows && Potree.Utils.updateVisible(this.arrows, 'dragging', false)
- //Potree.Common.waitTool.cancel('pathUpdateArrowDelay')
- })
-
- marker.addEventListener('drop',(e)=>{
- if(!this.isNew && this.arrows){
- Potree.Utils.updateVisible(this.arrows, 'dragging', true)
- Path.updateArrows(true)
- }
- //this.hideArrowUntilUpdate()
-
-
- if( e.button != THREE.MOUSE.LEFT )return
-
- viewer.inputHandler.dispatchEvent({type: 'measuring', v:false, cause:'stopDragging', situation:'dragging', object:this} )
-
- this.lastDropTime = Date.now()
- this.isNew || viewer.measuringTool.history.afterChange(this)
- this.setMarkerSelected(marker, 'unclick' );
- })
-
-
-
-
- let label = this.createMarkerLabel('')
- this.markerLabels = [...this.markerLabels.slice(0,index), label, ...this.markerLabels.slice(index, this.points.length)]
- this.setMarkerTitle(index, Potree.settings.isOfficial ? '' : 'point')
-
-
- super.addMarker(Object.assign(o, {index, marker }))
-
- //this.updateEdge()
- return marker
- };
-
- updateEndCaps(){
- let len = this.points.length
- let pts = this.geoPoints.length>0 ? this.geoPoints : this.points
- let len2 = pts.length
-
-
- this.endCaps.forEach((e,i)=>{
- Potree.Utils.updateVisible(e, 'hasPoints', len>0)
- if(len){
- if(len>1){
- let dir = i==0 ? new THREE.Vector3().subVectors(pts[1], pts[0])
- : new THREE.Vector3().subVectors(pts[len2-2], pts[len2-1])
- e.quaternion.copy(getMeshQuaInPath(dir))
- }else{
- i==0 ? e.quaternion.set(0,0,0,1) : e.quaternion.set(0,0,1,0) //两个半圆拼成一个圆点
- }
- e.position.copy(this.points[i==0 ? 0 : len-1])
- let s = this.halfPathWidth * 2.15
- e.scale.set(s,s,s)
- }
- })
- }
-
- setEditEnable(state){//是否显示可修改控件
- this.editEnable = !!state
- this.markers.forEach(e=>Potree.Utils.updateVisible(e,'editEnable', this.editEnable))
- this.endCaps.forEach((e,i)=>Potree.Utils.updateVisible(e,'editEnable', !this.editEnable))
- if(markerMats){//因为marker材质每条path都共用,所以isOfficial时请保证只有一条在编辑
- for(let i in markerMats){
- markerMats[i].fadeFar = this.fadeFar
- }
- }
-
- if(!state){
- this.updateEndCaps()
- }
-
- viewer.dispatchEvent('content_changed')
- }
-
- setAddOrRemPoint(state){//是否可以加减点, 此时不能拖拽marker
- this.addOrRemovePoint = !!state
- this.updateCursorState()
- }
- updateCursorState(){//可能在hover时修改addOrRemovePoint,也可能从marker和edge之间切换, 主要是addOrRemovePoint开关时要记得之前的hover状态
-
- CursorDeal[this.addOrRemovePoint ? 'add':'remove']('pen')
- if(this.addOrRemovePoint && !this.isNew){
- CursorDeal[this.hoverStates.edge ? 'add' : 'remove']('pen_addPoint')
- CursorDeal[this.hoverStates.marker ? 'add' : 'remove']('pen_delPoint')
- }else{
- CursorDeal.remove('pen_addPoint')
- CursorDeal.remove('pen_delPoint')
- }
- }
-
-
- setTitleVisi(label, v, reason=''){
- Potree.Utils.updateVisible(label, 'hideTitle-'+reason, v)
- viewer.dispatchEvent('content_changed')
- }
- setTitle(title=''){
- this.title = title
- this.titleLabel.setText(title)
- this.setTitleVisi(this.titleLabel.parent, title instanceof Array || title.trim() != '', 'noText')
- viewer.dispatchEvent('content_changed')
- }
- setMarkerTitle(index, title){
- if(!this.markerLabels[index])return
- this.markerLabels[index].originText = title
- this.markerLabels[index].setText(title)
- this.setTitleVisi(this.markerLabels[index], title instanceof Array || title.trim() != '', 'noText')
- viewer.dispatchEvent('content_changed')
- }
- editStateChange(state){ //拖动时被调用
- super.editStateChange(state)
- if(!state){
- this.editStateTimer = setTimeout(()=>{
- if(!this.isEditing){
- this.dispatchEvent({type:'editStateChange',state:false})
- }
- },100)
- }else{
- if(!this.isEditing){
- this.dispatchEvent({type:'editStateChange',state:true})
- clearTimeout(this.editStateTimer)
- }
- }
- this.isEditing = state
- }
-
- removePoint(index){
-
- if(index == -1){
- return //双击会这样,加了迅速删除, 可能因为没来得及删
- }
-
- viewer.measuringTool.history.beforeChange(this)
- this.removeMarker(index)
- this.hideArrowUntilUpdate()
- viewer.measuringTool.history.afterChange(this)
- this.dispatchEvent('changed')
-
- if(this.points.length == 0){
- //viewer.measuringTool.changeToAddState(this)
- viewer.measuringTool.startInsertion({resume:true, measure:this})
- }
- }
-
- setMarkerSelected(marker, state ){
-
- state == 'hover' && (marker.markerSelectStates.hover = true )
- state == 'unhover' && (marker.markerSelectStates.hover = false )
- state == 'click' && (marker.markerSelectStates.click = true ) //click or drag
- state == 'unclick' && (marker.markerSelectStates.click = false )
-
-
- if(marker.markerSelectStates.click){
- marker.material = getMarkerMat('drag')
- marker.renderOrder = marker.pickOrder = Potree.config.renderOrders.path.marker +1
- }else if(marker.markerSelectStates.hover){
- marker.material = getMarkerMat(this.addOrRemovePoint ? 'delete' : 'drag')
- marker.renderOrder = marker.pickOrder = Potree.config.renderOrders.path.marker +1
- }else{
- marker.material = getMarkerMat('default')
- marker.renderOrder = marker.pickOrder = Potree.config.renderOrders.path.marker
- }
-
- //marker.selected = absoluteState
-
- viewer.mapViewer && viewer.mapViewer.dispatchEvent('content_changed')
- viewer.dispatchEvent('content_changed')
- }
-
-
-
-
-
- setSelected( state, byList){
-
- if(state == 'click' && this.selectStates.click) return this.setSelected('unclick') //重复点击了
-
- state == 'hover' && (this.selectStates.hover = true, byList||this.dispatchEvent({type:'highlight', state:true}), CursorDeal.add('pointer'))
- state == 'unhover' && (this.selectStates.hover = false, byList||this.dispatchEvent({type:'highlight', state:false}), CursorDeal.remove('pointer'))
- state == 'click' && (this.selectStates.click = true, byList||this.dispatchEvent({type:'chose', state:true}) )
- state == 'unclick' && (this.selectStates.click = false, byList||this.dispatchEvent({type:'chose', state:false}) )
-
- /* this.arrows?.material.color.set(this.selectStates.click ? '#ffffff' : this.color);
-
- ([this.edge, this.endCaps[0].children[0]].forEach(e=>{
- e.material.opacity = this.selectStates.click ? 1 : this.selectStates.hover ? 0.8 : 0.6
- e.material.color.set(this.selectStates.click ? '#00C8AF' : this.color)
- })) */
- this.updateSelectStyle()
-
- state == 'click' && setTimeout(()=>{
- viewer.addEventListener('global_click', (e)=>{ //再点击取消
- this.setSelected('unclick')
- }, {once:true} )
- },10)
-
- viewer.dispatchEvent('content_changed')
- viewer.mapViewer && viewer.mapViewer.dispatchEvent('content_changed')
- }
-
-
- updateSelectStyle(){
-
-
- let c = new THREE.Color().set(this.color).getHSL({ h: 0, s: 0, l: 0 })
-
- let color, arrowColor
- if(this.selectStates.click){
- color = '#00C8AF'
- arrowColor = '#ffffff'
- }else if(this.selectStates.hover){
- color = new THREE.Color().setHSL(c.h, c.s, c.l - 0.1 )
- arrowColor = new THREE.Color().setHSL(c.h, c.s, c.l >= 0.4 ? c.l - 0.3 : c.l + 0.3 )
- }else{
- arrowColor = new THREE.Color().setHSL(c.h, c.s, c.l >= 0.4 ? c.l - 0.3 : c.l + 0.3 )
-
- color = this.color
- }
-
- if(this.arrows){
- this.arrows.material.color.set(arrowColor);
- }else{
- this.edge.material.uniforms.mapColor.value.set(arrowColor)
- }
- ([this.edge, this.endCaps[0].children[0]].forEach(e=>{
- e.material.color.set(color)
- }))
- }
-
- removeMarker(index ){
-
- super.removeMarker(index)
-
- this.points_datasets.splice(index, 1);
- this.dataset_points && this.dataset_points.splice(index, 1)
-
-
- let labelIndex = index
- if(this.markerLabels[labelIndex]){
- this.markerLabels[labelIndex].dispose()
- this.markerLabels.splice(labelIndex, 1);
- }
-
- this.update({index: this.getIndex(index, -1)});
- this.dispatchEvent({type: 'marker_removed', measurement: this});
-
-
- }
-
- setPosition(index, position) {
- super.setPosition(index, position)
- let event = {
- type: 'marker_moved',
- measure: this,
- index: index,
- position: position.clone()
- };
- this.dispatchEvent(event);
- }
-
-
-
-
-
-
- setFontSize(fontsize){
- this.fontsize = fontsize
-
- this.markerLabels.concat(this.titleLabel).forEach(e=>{
- e.fontsize = fontsize
- e.updateTexture();
- })
-
- viewer.dispatchEvent('content_changed')
- }
-
-
- setFadeFar(far){//消失距离
- if(far == void 0) far = 0 //不设置
- this.traverse((e)=>{
- //if(e.name == 'marker')return //因为marker材质共用的所以不改。因此正式编辑时(有marker时)别设置消失距离。
- if(e.material?.uniforms?.fadeFar){
- e.material.fadeFar = far
- }
- })
- this.fadeFar = far
- if(markerMats){//因为marker材质每条path都共用,所以isOfficial时请保证只有一条在编辑
- for(let i in markerMats){
- markerMats[i].fadeFar = this.fadeFar
- }
- }
- viewer.dispatchEvent('content_changed')
- }
-
-
-
- dispose(){
- super.dispose()
- this.titleLabel.dispose()
- this.markerLabels.forEach(e=>e.dispose())
- this.arrows?.dispose()
- this.arrows?.material.dispose()
- this.edge.geometry.dispose()
- this.edge.material.dispose()
-
- this.setAddOrRemPoint(false) //cursor recover
- this.dispatchEvent('disposed')
-
-
- }
-
-
- updateArrowRepeat(){
- if(!this.edge.material.map)return
- this.edge.material.map.repeat.x = Math.round(this.totalLength / this.halfPathWidth * 0.5) * (this.reverse ? -1 : 1)
- this.edge.material.map.needsUpdate = true
- this.edge.material.setUV()
- }
-
-
- setArrowDisplay(show){
- if(Potree.settings.pathSmooth){
- if(show){
- let map = texLoader.load( Potree.resourcePath+'/textures/arrow.png',()=>{
- viewer.dispatchEvent('content_changed')
- })
- map.anisotropy = 2
- map.wrapS = THREE.RepeatWrapping
- map.repeat.set(10,1.3)
- map.offset.set(0,-0.15)
- this.edge.material.map = map
- this.updateArrowRepeat()
- }else{
- this.edge.material.map?.dispose()
- this.edge.material.map = null
- }
- }else{
- if(show){
- if(!this.arrows){
- let map = texLoader.load( Potree.resourcePath+'/textures/arrow.png')
- //map.anisotropy = 2
- map.generateMipmaps = false;
- map.minFilter = THREE.LinearFilter //防止边缘黑边, 但会造成锯齿
-
-
- let material = new DepthBasicMaterial(Object.assign({}, depthProps, {
- map, transparent:true, side:2, fadeFar:this.fadeFar , color:this.selectStates.click ? '#ffffff' : this.color
- }))
- //let material = new THREE.MeshBasicMaterial({map )})
- this.arrows = new THREE.InstancedMesh(planeGeo, material, arrowCountMax); //会自动向shader添加define和instanceMatrix
- this.arrows.renderOrder = Potree.config.renderOrders.path.edge
- this.add(this.arrows)
- //Potree.Utils.setObjectLayers(this.arrows, 'measure' )
- }
- this.updateSelectStyle()
- }
- if(this.arrows){
- Potree.Utils.updateVisible(this.arrows, 'show', show)
- show && Path.waitUpdateArrows()
- }
- }
- viewer.dispatchEvent('content_changed')
- }
-
-
- setReverse(reverse){
- if(this.reverse != reverse){
- this.reverse = reverse
- if(Potree.settings.pathSmooth){
- this.updateArrowRepeat()
- }else{
- Path.updateArrows(true)
- }
- }
- }
-
- hideArrowUntilUpdate(){
- this.arrows && Potree.Utils.updateVisible(this.arrows, 'changing', false)
- Path.waitUpdateArrows()
-
- }
-
-
-
- static waitUpdateArrows(){
- if(Potree.settings.pathSmooth)return
- Potree.Common.waitTool.wait('pathUpdateArrowDelay',()=>{
- viewer.scene.measurements.forEach(e=>e instanceof Path && e.arrows && Potree.Utils.updateVisible(e.arrows, 'changing', true) )
-
- Path.updateArrows(true)
- viewer.dispatchEvent('content_changed')
- },300) //为了防止不停点击不停更新,所以隐藏一下不变了再更新
- }
-
-
- static updateArrows(force){
- //console.error('updateArrows?')
- const far = math.linearClamp(Potree.fpsRendered2, [10, 60], [200, 300]); //几乎看不见了
-
-
-
- let paths = viewer.scene.measurements.filter(e=>e instanceof Path && e.arrows?.visible)
- let waitAdd = []
- let minDisSq = Infinity
-
-
- let startTime = performance.now()
-
- let lines = []
- paths.forEach(path=>{
- let len = path.points.length
-
- let far_ = path.halfPathWidth * far
- far_ = path.fadeFar ? Math.min(far_, path.fadeFar * 1.2) : far_
- let farSquared_ = far_ * far_
-
-
- for(let i=0; i<len-1; i++){
- let thisPoint = path.points[i];
- let nextPoint = path.points[i+1]
- let line = new THREE.Line3(thisPoint, nextPoint)
- let closetPoint = line.closestPointToPoint( viewer.mainViewport.view.position, true, new THREE.Vector3() ) //过相机到line的垂足,限制在线段内
-
- let disSq = closetPoint.distanceToSquared(viewer.mainViewport.view.position)
- if(disSq > farSquared_ && lastArrowCount > 0)continue
-
- minDisSq = Math.min(minDisSq, disSq)
-
- lines.push({line, disSq, path})
- }
- })
-
- let spaceDis = THREE.Math.clamp( math.toPrecision(Math.pow(minDisSq, 0.1) * 5, 1), 5, 15) //箭头之间的间距,适当调节稀疏
- spaceDis *= math.linearClamp(Potree.fpsRendered2, [10, 60], [3, 1])
-
- if((lastArrowCamPos && math.closeTo(lastArrowCamPos, viewer.mainViewport.view.position, 2) || lines.length == 0) && !force)return //很远的时候lines空的不更新(不清空)
- lastArrowCamPos = viewer.mainViewport.view.position.clone()
-
-
- viewer.scene.measurements.forEach(e=>e instanceof Path && e.arrows && (e.arrows.count = 0)) //归零
-
-
-
-
- lines.sort((a,b)=>{return a.disSq - b.disSq}) //尽快收集好就近的,使后加的都比waitAdd最后一个大,减少比较
-
- lines.forEach(info=>{
-
- if(waitAdd[arrowCountMax-1] && info.disSq > waitAdd[arrowCountMax-1].disSq)return //线段最近点已经超过当前list中最远的那个
-
- info.dir = new THREE.Vector3().subVectors(info.line.end, info.line.start).normalize()
- let lineLen = info.line.end.distanceTo(info.line.start)
-
- let spaceDis_ = info.path.halfPathWidth * spaceDis //跟随箭头缩放
-
-
- let sliceCount = Math.round( lineLen / spaceDis_) //分段
-
- if(sliceCount < 1) return;
- if(sliceCount == 1) sliceCount = 2//至少为2
-
-
- let arrowCount = sliceCount - 1
-
- let sliceLen = lineLen / sliceCount
- let j = arrowCount
-
- while(j > 0){
-
- let pos = info.line.start.clone().add(info.dir.clone().multiplyScalar(j*sliceLen))
- let disSq = pos.distanceToSquared(viewer.mainViewport.view.position)
- j--;
- if(waitAdd[arrowCountMax-1] && disSq > waitAdd[arrowCountMax-1].disSq)continue
- //按从小到大排好。(不知道这种事先排好序的方法 和全部加入最后排序选前面的 相比哪个耗时。 因为冒泡排序更快,但如果总数很多会不会慢?)
- let newItem = {pos, disSq, lineInfo:info}
- let index = waitAdd.findIndex(e=>e.disSq>disSq)
- if(index == -1){
- waitAdd.push(newItem)
- }else{
- waitAdd = [...waitAdd.slice(0,index), newItem, ...waitAdd.slice(index, waitAdd.length)]
- }
- waitAdd.length>arrowCountMax && waitAdd.pop()
-
- }
- })
-
-
- let scaleMap = new Map
-
- waitAdd.forEach((e,i)=>{
- let scaleQuaMatrix = e.lineInfo.scaleQuaMatrix
- if(!scaleQuaMatrix){
- let scaleMatrix = scaleMap.get(e.lineInfo.path)
- if(!scaleMatrix){
- let s = e.lineInfo.path.halfPathWidth * 1.6
- scaleMatrix = new THREE.Matrix4().scale(new THREE.Vector3((e.lineInfo.path.reverse ? -1 : 1 ) *s,s,s))
- scaleMap.set(e.lineInfo.path, scaleMatrix)
-
- }
- let qua = getMeshQuaInPath(e.lineInfo.dir)//math.getQuaFromPosAim( new THREE.Vector3, e.lineInfo.dir ).multiply(quaBase)
- let quaMatrix = new THREE.Matrix4().makeRotationFromQuaternion(qua)
- scaleQuaMatrix = e.lineInfo.scaleQuaMatrix = new THREE.Matrix4().multiplyMatrices(quaMatrix, scaleMatrix)
- }
-
-
- let matrix = scaleQuaMatrix.clone().setPosition(e.pos)
- e.lineInfo.path.arrows.count ++
- e.lineInfo.path.arrows.setMatrixAt(e.lineInfo.path.arrows.count-1, matrix)
- e.lineInfo.path.arrows.instanceMatrix.needsUpdate = true;
- })
-
-
- lastArrowCount = waitAdd.length
- //console.log('updateArrows spaceDis', spaceDis, 'count', waitAdd.length, 'cost', performance.now() - startTime)
-
- }
-
-
-
- }
- Path.prototype.cloneMarker = Measure.prototype.cloneMarker
- Path.prototype.updateDatasetBelong = Measure.prototype.updateDatasetBelong
- Path.prototype.reDraw = Measure.prototype.reDraw
- /*
- 没有intersect的点的dataset_point怎么赋值
- 1 lonlat : 因为说是加在地图上。 但万一用户在添加完path之后才设置经纬度,这个点可能就飘到很远的地方? 由于模型当前的位置都是本地坐标而不是经纬度,所以模型不会跟着一起飘远。
- 2 就近的datasetId : 但如果path全部点都在地图上, 现场只有一个离他远的模型,但它仍被判断在该模型上。之后在路径边加了其他模型。 当它的所属模型被删除后它就像意外消失了……
- 3 本地坐标 : 当模型移动后该点不会跟着移动。 写起来比上一条麻烦点,但逻辑不容易误解。
- 目前用的3
- */
- /*
- const plane = new THREE.Plane(normal, dis);
- viewer.renderer.localClippingEnabled = true;
- material.clippingPlanes = [ localPlane ]
- #include <clipping_planes_pars_fragment>
- #include <clipping_planes_fragment>
- */
-
|