three.js:点到多边形的最短距离计算

    科技2022-08-20  253

    1. 多边形的顶点存储在pointsAll数组中,允许场景中存在多个多边形,在数组中通过[1.111, 1.111, 1.111]作为不同多边形的分割点。如图,有两个多边形,顶点数据为:

    (7) [Vector3, Vector3, Vector3, Vector3, Vector3, Vector3, Vector3] 0: Vector3 {x: -51.000000000000014, y: 0, z: 8.49049636423993} 1: Vector3 {x: 69.00000000000001, y: 0, z: 21.490496364233458} 2: Vector3 {x: 79.99999999999999, y: 0, z: 153.49049636416737} 3: Vector3 {x: -45.99999999999997, y: 0, z: 191.49049636414838} 4: Vector3 {x: -94.99999999999999, y: 0, z: 138.4904963641749} 5: Vector3 {x: -87.00000000000001, y: 0, z: 55.49049636421642} 6: Vector3 {x: 1.111, y: 1.111, z: 1.111} 7: Vector3 {x: -209.99999999999997, y: 0, z: -209.509503635651} 8: Vector3 {x: -114.99999999999999, y: 0, z: -208.50950363565144} 9: Vector3 {x: -69.00000000000001, y: 0, z: -89.50950363571106} 10: Vector3 {x: -145, y: 0, z: -21.509503635745077} 11: Vector3 {x: -208, y: 0, z: -60.50950363572556} 12: Vector3 {x: -250.99999999999997, y: 0, z: -130.5095036356905} 13: Vector3 {x: 1.111, y: 1.111, z: 1.111} length: 14 __proto__: Array(0)

     2. 鼠标点击页面时,调用函数inFenceDetect和closeFenceDetect

    inFenceDetect(pointsAll, cirFenceAll, clickPos) 

    功能:检测鼠标点击的点是否落在多边形电子围栏内参数:pointsAll是所有多边形的顶点集合,如步骤1所示;cirFenceAll是圆形电子围栏的位置和半径,此处忽略,以[0, 0, 0]代替;clickPos是鼠标的点击位置转换到three.js世界坐标后的值返回值:true或者false,true代表鼠标点击位置处于多边形电子围栏内,反之则在围栏外

    closeFenceDetect(pointsAll, cirFenceAll, clickPos, 10)

    功能:检测鼠标点击位置是否在多边形电子围栏附近参数:pointsAll是所有多边形的顶点集合,如步骤1所示;cirFenceAll是圆形电子围栏的位置和半径,此处忽略,以[0, 0, 0]代替;clickPos是鼠标的点击位置转换到three.js世界坐标后的值;参数值10是阈值,代表鼠标点击位置与多边形的最小距离小于10个像素时,判定为接近电子围栏,该值可以随意设定返回值:字符串IN / OUT / CLOSE,IN表示点击位置在多边形电子围栏内,OUT表示点击点在多边形电子围栏外面且不接近;CLOSE表示点击位置距离多边形电子围栏10个像素以内时,判定为接近电子围栏 container.onclick = function(){ var res, str, dis; var clickPos = dom2world(event.clientX,event.clientY); res = inFenceDetect(pointsAll, cirFenceAll, clickPos); dis = closeFenceDetect(pointsAll, cirFenceAll, clickPos, 10); if (res) {str='IN';} else {str='OUT';} console.log('在多边形内,结果:'+str) console.log('靠近多边形,结果:'+dis) // console.log( pointsAll ) }

    3. 函数体

    inFenceDetect(pointsAll, cirFenceAll, clickPos) 

    function inFenceDetect(PointsAll, cirFenceAll, p){ /* 1. 从数据库或者本地获取一共有M个圆形,N个多边形 2. 获取每个圆形的圆心和半径,获取每个多边形的顶点 3. 测试当前点是否在圆形中 4. 测试当前点是否在多边形中 有PointsAll中包含多个多边形,以1.111分割 PointsSeg表示某个具体的多边形顶点集合 segFlag表示某个多边形第一个起始顶点在pointsAll中的索引值 res为总的返回结果,true或者false segRes为某个多边形返回的判断结果,true或者false segRes之间是或关系,只要有一个segRes返回真,就说明多边形电子围栏被触发了 */ var count = PointsAll.length, PointsSeg=[], segFlag=0, res=0, segRes; for (var i = 0; i < PointsAll.length; i++) { if (PointsAll[i].x==1.111 && PointsAll[i].y==1.111 && PointsAll[i].z == 1.111) { for (var j = segFlag; j < i; j++) { PointsSeg.push( PointsAll[j] ); } segRes = isInsidePoly(PointsSeg,p); while(PointsSeg.length>0){ PointsSeg.pop(); } segFlag=i+1; res = res|segRes; i = i+1; } } for (var i = 0; i < cirFenceAll.length; i++) { var resCir = isInsideCircle([cirFenceAll[i][0],cirFenceAll[i][1]], cirFenceAll[i][2], p); res = res|resCir; } return res; // isInsidePoly(); } function isInsidePoly(Points,p){ //判断一个点是否在Points组成的多边形内 //1. 获取多边形的顶点数目 //2. 如果多边形顶点小于3,直接返回0。否则继续往下判断 //3. 以射线法探测顶点,判断顶点是否在多边形内。 // 判断规则,从p点引射线,如果与多边形相交为奇数个点,则在多边形内,否则在多边形外。 var count = Points.length; if(count < 3){ return false; } var result = false; for(var i = 0, j = count - 1; i < count; i++) { var p1 = Points[i]; var p2 = Points[j]; if(p1.x < p.x && p2.x >= p.x || p2.x < p.x && p1.x >= p.x) { if(p1.z + (p.x-p1.x) / (p2.x-p1.x) * (p2.z-p1.z) < p.z) { result = !result; } } j = i; } return result; } function isInsideCircle(pos, radius, p){ //判断点p到圆形电子围栏的距离,距离小于半径radius,证明越过了电子围栏,违规,返回true console.log(pos) console.log(radius) var dis = Math.pow(p.x-pos[0],2) + Math.pow(p.z-pos[1],2); var dis = Math.sqrt(dis); if(dis<radius) return true; else return false; }

    closeFenceDetect(pointsAll, cirFenceAll, clickPos, 10)

    function closeFenceDetect(PointsAll, cirFenceAll, p, threshold){ //1. 检测当前点是否接近多边形电子围栏 //2. 距离电子围栏近 //返回值 在电子围栏中IN 靠近CLOSE 不靠近OUT var count = PointsAll.length, PointsSeg=[], segFlag=0, res=0, segRes=0; res = inFenceDetect(PointsAll, cirFenceAll, p); if (res==true) { return 'IN'; } for (var i = 0; i < PointsAll.length; i++) { if (PointsAll[i].x==1.111 && PointsAll[i].y==1.111 && PointsAll[i].z == 1.111) { for (var j = segFlag; j < i; j++) { PointsSeg.push( PointsAll[j] ); } segRes = isClosePoly(PointsSeg,p); console.log('segRes: '+segRes) while(PointsSeg.length>0){ PointsSeg.pop(); } segFlag=i+1; i = i+1; if( segRes<threshold ){ return 'CLOSE'; } } } return 'OUT'; } function isClosePoly(points,p){ /* points: 多边形的所有顶点 p:多边形外一点 点p可以和相邻的两个顶点组成一个三角形; 如果三角形三个角都为锐角,则p到该边最小值为垂线; 如果点p对应的角为钝角,则p到该边最小值为垂线; 如果点p为锐角,其余两个角中任意一个为钝角,则p到该边最小值为p到两个顶点的距离的最小值 */ var a,b,c; //三条边 var A,B,C; //三个角 var dis=0, dism=0, dis_temp; //点到线段的距离;最小距离;记录第一个数值 for (var i = 0; i < points.length; i++) { if ( i<points.length-1 ) { a = Math.sqrt( Math.pow( p.x-points[i+1].x,2 ) + Math.pow( p.z-points[i+1].z,2 ) ); b = Math.sqrt( Math.pow( p.x-points[i].x,2 ) + Math.pow( p.z-points[i].z,2 ) ); c = Math.sqrt( Math.pow( points[i].x-points[i+1].x,2 ) + Math.pow( points[i].z-points[i+1].z,2 ) ); A = Math.acos( (b*b+c*c-a*a)/(2*b*c) ) * 180/3.14159; B = Math.acos( (a*a+c*c-b*b)/(2*a*c) ) * 180/3.14159; C = Math.acos( (a*a+b*b-c*c)/(2*a*b) ) * 180/3.14159; if( C>90 ){ dis = Math.sqrt( b*b - Math.pow( (b*b+c*c-a*a)/(2*c),2) ); }else if( A<90 && B<90 ){ dis = Math.sqrt( b*b - Math.pow( (b*b+c*c-a*a)/(2*c),2) ); }else if( A>90 || B>90 ){ a<b?dis=a:dis=b; } }else{ a = Math.sqrt( Math.pow( p.x-points[0].x,2 ) + Math.pow( p.z-points[0].z,2 ) ); b = Math.sqrt( Math.pow( p.x-points[i].x,2 ) + Math.pow( p.z-points[i].z,2 ) ); c = Math.sqrt( Math.pow( points[i].x-points[0].x,2 ) + Math.pow( points[i].z-points[0].z,2 ) ); A = Math.acos( (b*b+c*c-a*a)/(2*b*c) ) * 180/3.14159; B = Math.acos( (a*a+c*c-b*b)/(2*a*c) ) * 180/3.14159; C = Math.acos( (a*a+b*b-c*c)/(2*a*b) ) * 180/3.14159; if( C>90 ){ dis = Math.sqrt( b*b - Math.pow( (b*b+c*c-a*a)/(2*c),2) ); }else if( A<90 && B<90 ){ dis = Math.sqrt( b*b - Math.pow( (b*b+c*c-a*a)/(2*c),2) ); }else if( A>90 || B>90 ){ a<b?dis=a:dis=b; } } i==0?dism=dis:dism=dism; dis<dism?dism=dis:dism=dism; console.log('角 A='+A+',B='+B+',C='+C) console.log('边 a='+a+',b='+b+',c='+c) console.log('dis = '+dis) console.log('dism = '+dism) } return dism; }

    4. 如何计算点到多边形的最短距离

    假设多边形有5条边,多边形外一点为C,那么最短距离无疑是点C到多边形5条边的距离的最小值。注意,注意,注意,这5条边是线段,而不是直线。以其中一条边AB为例:当∠A和∠B为锐角时,点C到线段AB的最短距离为垂直距离;当∠A和∠B中有一个为钝角时,点C到线段AB的最短距离为min{AC, BC}。for循环重复5遍上述步骤,就可以求出点C到五边形的最短距离了。

    Processed: 0.012, SQL: 9