欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 19:41:42
...

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)

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

 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到五边形的最短距离了。

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

相关标签: 前端